├── VHD_Files └── the-VM-will-be-create-here.txt ├── VHD_Reference └── place-here-the-NanoServerDataCenter.vhd-file ├── pics ├── 1.PNG ├── 2.PNG ├── 3.PNG ├── 4.PNG ├── 5.PNG ├── 6.PNG ├── 7.PNG ├── 8.PNG ├── 9.PNG ├── logo.jpg └── logoSwarmMSKit.png ├── SwarmMSKit ├── SwarmMSKit.psd1 └── SwarmMSKit.psm1 ├── LICENSE.md ├── README.md └── SwarmMSKitProvisioning.ps1 /VHD_Files/the-VM-will-be-create-here.txt: -------------------------------------------------------------------------------- 1 | . 2 | -------------------------------------------------------------------------------- /VHD_Reference/place-here-the-NanoServerDataCenter.vhd-file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pics/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/1.PNG -------------------------------------------------------------------------------- /pics/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/2.PNG -------------------------------------------------------------------------------- /pics/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/3.PNG -------------------------------------------------------------------------------- /pics/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/4.PNG -------------------------------------------------------------------------------- /pics/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/5.PNG -------------------------------------------------------------------------------- /pics/6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/6.PNG -------------------------------------------------------------------------------- /pics/7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/7.PNG -------------------------------------------------------------------------------- /pics/8.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/8.PNG -------------------------------------------------------------------------------- /pics/9.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/9.PNG -------------------------------------------------------------------------------- /pics/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/logo.jpg -------------------------------------------------------------------------------- /pics/logoSwarmMSKit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/HEAD/pics/logoSwarmMSKit.png -------------------------------------------------------------------------------- /SwarmMSKit/SwarmMSKit.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | GUID="{8549a57e-441d-46e6-a3d5-244e270aa3ac}" 3 | Author="Fouzi BOUKEZZOULA" 4 | CompanyName="Fouzi BOUKEZZOULA" 5 | Copyright="© FB 2017" 6 | RootModule = "SwarmMSKit.psm1" 7 | ModuleVersion="1.0.0.3" 8 | PowerShellVersion="3.0" 9 | ClrVersion="4.0" 10 | FunctionsToExport="PurgeTempMountFolder","Pause","Create-Set-VM-Hyper-V","Enabled-VM-Hyper-V","Wait-WinRM-Reachable","DockerInstallation","VaultServer-PrivateRegistry-UCP","ConsulServer","ConsulAgent","SwarmWorker","SwarmManager","NanoSetup","Set-VMNetworkConfiguration","DockerSwarmMSKitWithTLSAuthentication","SwarMSKit-Check" 11 | HelpInfoUri="https://github.com/fboukezzoula/SwarmMSKit" 12 | } 13 | 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Fouzi BOUKEZZOULA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ScreenShot](https://raw.githubusercontent.com/fboukezzoula/SwarmMSKit/master/pics/logoSwarmMSKit.png) 2 | 3 | 4 | # SwarmMSKit 5 | 6 | 1. [Overview](#SwarmMSKit-Overview) 7 | 8 | 2. [SwarmMSKit Description - What the tool does and why it is useful](#SwarmMSKit-Description) 9 | 3. [Setup - The basics of getting started with SwarmMSKit] 10 | * [Setup requirements](#SwarmMSKit-Setup-requirements) 11 | * [Beginning with SwarmMSKit](#SwarmMSKit-Beginning) 12 | 4. [Usage - SwarmMSKit](#SwarmMSKit-Usage) 13 | 5. [Usage - SwarmMSKit with TLS Authentication](#SwarmMSKit-Usage) 14 | 6. [Video Tutorial - SwarmMSKit-Youtube channel] 15 | 16 | SwarmMSKit - Overview 17 | ----- 18 | 19 | 10 minutes for deploying a full Cluster Swarm Microsoft NanoServer with all tools on Hyper-V ! 20 | 21 | Provisioning a Full MS NanoServer Cluster Swarm on Hyper-V + Consul + Vault + Private Registry + Management UI for your cluster ... all the VMs can be integrated in an Active Directory Domain or you can use a Local account. 22 | 23 | SwarmMSKit can configure your Docker Swarm for TLS too. You have to choose only your hostname prefix and TCP/IP configuration for the Nanoserver VM. 24 | 25 | SwarmMSKit - Description 26 | ----- 27 | 28 | Provisioning a Full MS NanoServer Cluster Swarm on Hyper-V with Consul Hashicorp software as Discovery Service, Vault Hashicorp software as a Secret Management Store and a Private Docker Registry host on Nexus OSS software. 29 | 30 | All VMs can be integrated in an Active Directory Domain during the provisioning or you can use a local windows account. 31 | 32 | All the VM NanoServer will running on Hyper-V. 33 | 34 | All the VMs NanoServer will be up to date (latest KBs/Hotfixs) and will hosted the last docker OSImage nanoserver (microsoft/nanoserver, nanoserver tagged). 35 | 36 | You can disable the Microsoft Firewall or use the Firewall : the SwarmMSKit will create for you all the firewall rules (more than 16 rules) for enabled the access to the Docker Daemon, Swarm, Consul, Vault, Private Registry, Management UI, WinRM, File Sharing, etc .... 37 | 38 | By default (variable : $global:EnabledDockerDaemonTLS = $True) the SwarmMSKit tool will automatically configure your Docker Swarm for TLS, all the Docker Engine hosts (client, swarm manager(s) and swarm workers) have a copy of the CA’s certificate as well as their own key-pair signed by the CA. 39 | In this case, all the client certificates are automatically generated in this folder : 40 | 41 | * $env:USERPROFILE\\.SwarmMSKit 42 | 43 | Finally, the UI Swarm Administration Web (Portainer) and the UI Nexus OSS (Private Registry) are automatically open in your default web browser and we generated in your desktop a file called SwarmMSKIT-Check.cmd for testing the installation and configuration of all the tools (consul, vault, docker swarm and running a container to the swarm !) 44 | 45 | * $env:USERPROFILE\Desktop\SwarmMSKIT-Check.cmd 46 | 47 | We use the latest supported Docker Daemon Engine, launched this last 18 January 2017 (1.13), Swarm (1.2.6 : build the swarm.exe file with the official Docker Swarm GitHub source), Vault (0.6.4), Consul (0.7.2) and Nexus OSS (3.2.0-01) versions. We use the latest OSImage docker Microsoft/NanoServer (Image ID : d9bccb9d4cac). 48 | 49 | Notice that I've built locally the swarm.exe binary with the latest version of this docker/swarm source codes : 50 | https://github.com/docker/swarm/releases/tag/v1.2.6 51 | 52 | (below examples without the TLS Enabled : EnabledDockerDaemonTLS = $False) 53 | 54 | docker -H tcp://10.1.0.24:2375 version : 55 | Client: 56 | Version: 1.13.0 57 | API version: 1.25 58 | Go version: go1.7.3 59 | Git commit: 49bf474 60 | Built: Wed Jan 18 16:20:26 2017 61 | OS/Arch: windows/amd64 62 | 63 | Server: 64 | Version: 1.13.0 65 | API version: 1.25 (minimum version 1.24) 66 | Go version: go1.7.3 67 | Git commit: 49bf474 68 | Built: Wed Jan 18 16:20:26 2017 69 | OS/Arch: windows/amd64 70 | Experimental: false 71 | 72 | 73 | docker -H tcp://10.1.0.24:2017 info 74 | 75 | Containers: 0 76 | Running: 0 77 | Paused: 0 78 | Stopped: 0 79 | Images: 2 80 | Server Version: swarm/1.2.6 81 | Role: primary 82 | Strategy: spread 83 | Filters: health, port, containerslots, dependency, affinity, constraint, whitelist 84 | Nodes: 2 85 | Nano-Worker-01: 10.1.0.25:2375 86 | └ ID: 6WOI:CJAO:QPL2:D7ID:XD4M:RAQ3:O4AM:374O:GEC6:V2FW:YYUX:APNF 87 | └ Status: Healthy 88 | └ Containers: 0 (0 Running, 0 Paused, 0 Stopped) 89 | └ Reserved CPUs: 0 / 4 90 | └ Reserved Memory: 0 B / 2.1 GiB 91 | └ Labels: kernelversion=10.0 14393 (14393.206.amd64fre.rs1_release.160915-0644), operatingsystem=Windows Server 2016 Datacenter, storagedriver=windowsfilter 92 | └ UpdatedAt: 2017-02-08T15:51:52Z 93 | └ ServerVersion: 1.13.0 94 | Nano-Worker-02: 10.1.0.26:2375 95 | └ ID: JVFE:5MS7:XRWC:OQA4:2QZJ:LUBA:GRIJ:AHRX:5SM7:YA25:LXGC:VLXK 96 | └ Status: Healthy 97 | └ Containers: 0 (0 Running, 0 Paused, 0 Stopped) 98 | └ Reserved CPUs: 0 / 4 99 | └ Reserved Memory: 0 B / 2.1 GiB 100 | └ Labels: kernelversion=10.0 14393 (14393.206.amd64fre.rs1_release.160915-0644), operatingsystem=Windows Server 2016 Datacenter, storagedriver=windowsfilter 101 | └ UpdatedAt: 2017-02-08T15:51:44Z 102 | └ ServerVersion: 1.13.0 103 | Plugins: 104 | Volume: 105 | Network: 106 | Swarm: 107 | NodeID: 108 | Is Manager: false 109 | Node Address: 110 | Kernel Version: 10.0 14393 (14393.206.amd64fre.rs1_release.160915-0644) 111 | Operating System: windows 112 | Architecture: amd64 113 | CPUs: 8 114 | Total Memory: 4.199 GiB 115 | Name: Nano-Manager-01 116 | Docker Root Dir: 117 | Debug Mode (client): false 118 | Debug Mode (server): false 119 | WARNING: No kernel memory limit support 120 | Experimental: false 121 | Live Restore Enabled: false 122 | 123 | vault status -address=http://10.1.0.24:8200 124 | 125 | Sealed: false 126 | Key Shares: 5 127 | Key Threshold: 3 128 | Unseal Progress: 0 129 | Version: 0.6.4 130 | Cluster Name: vault-cluster-80d388b2 131 | Cluster ID: 43494c69-f954-aab3-47d1-6663bc1c6c1f 132 | 133 | High-Availability Enabled: true 134 | Mode: active 135 | Leader: http://10.1.0.24:8200 136 | 137 | consul members --rpc-addr=10.1.0.24:8400 138 | 139 | Node Address Status Type Build Protocol DC 140 | Nano-Manager-01 10.1.0.24:8301 alive server 0.7.2 2 nano-swarm 141 | Nano-Worker-01 10.1.0.25:8301 alive client 0.7.2 2 nano-swarm 142 | Nano-Worker-02 10.1.0.26:8301 alive client 0.7.2 2 nano-swarm 143 | 144 | 145 | For the NanoServer VHD reference, we will use the latest and official Microsoft Nanoserver VHD : 146 | https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016 147 | 148 | (you have to SignIn, accept the Microsoft licence and download the VHD file ...) 149 | 150 | But I have already prepared a 'ready to use' Nanoserver VHD for the SwarmMSKit : mount/unmount/djoin with dism tool for injecting the latest KB/Hotfixs, binaries (curl, docker, swarm, consul, vault, etc...), install the Windows features (containers, compute Hyper-V, IIS, etc...). You have only to download this file. 151 | 152 | This prepared VHD reference is up to date, the below Powershell script has already been performed on it : 153 | 154 | $ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession 155 | Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates 156 | Restart-Computer; exit 157 | 158 | Initial Installation of Docker in the NanoServer (already done) 159 | 160 | Install-Module -Name DockerMsftProvider -Repository PSGallery -Force 161 | Install-Package -Name docker -ProviderName DockerMsftProvider 162 | Restart-computer -force 163 | Import-Module DockerMsftProvider 164 | start-service docker 165 | docker pull microsoft/nanoserver 166 | docker tag microsoft/nanoserver nanoserver 167 | 168 | SwarmMSKit - Setup requirements 169 | ----- 170 | 171 | Any Windows OS whit the feature Hyper-V Installed like : 172 | Windows 10 Pro or Entreprise Version 173 | Windows Server 2008 R2 Standard or Datacenter 174 | Windows Server 2016 Standard or Datacenter 175 | 176 | (tests and validate on Windows Server 2016 and Windows 10 Professional, any feedback are welcome !) 177 | 178 | Powershell Version v3 or higher 179 | 180 | Clone this SwarmMSKit (unzip it on a folder called for example "SwarmMSKit") : 181 | https://github.com/fboukezzoula/SwarmMSKit/archive/master.zip 182 | 183 | Then download the NanoServerDataCenter.vhd file from this Google Drive address : 184 | https://drive.google.com/open?id=0BzqZR1dT_FQgRFJFZk1xcDZKOEk 185 | 186 | in a folder (parameter: $global:Model_NanoServerDataCenter) 187 | 188 | And that's it, you are ready to deploy your Cluster Swarm Full MS NanoServer ! 189 | 190 | SwarmMSKit - Beginning 191 | ----- 192 | 193 | Set constants/variables in the SwarmMSKitProvisioning.ps1 file : 194 | 195 | $global:WorkDir = "C:\SwarmMSKit" 196 | $global:VMPath = "$global:WorkDir\VHD_Files" 197 | $global:Model_NanoServerDataCenter = "$global:WorkDir\VHD_Reference\NanoServerDataCenter.vhd" 198 | 199 | IP Configuration of your first VM NanoServer and your VM Switch Hyper-V configuration : 200 | 201 | IPAddress = "10.1.0.24" 202 | GatewayAddress = "10.1.0.1" 203 | SubnetMask = "255.255.255.0" 204 | DNSAddresses = "10.1.0.1" 205 | Subnet = "10.1.0." 206 | VMSwitch = "InternalNetwork" 207 | 208 | System Configuration for our NanoServer VM, (below, we will set 2 vCPU and 2 Go RAM for each VM) : 209 | 210 | VMProcessor = 2 211 | VMRam = 2048MB 212 | 213 | Type Of Authentication to the NanoServer : Local Account (default value: Local) or AD Account (AD) ? 214 | 215 | $global:AuthenticationType = "AD" 216 | 217 | Name prefix (Netbios/Hostname of the NanoServer and AD computer Names & Name in Hyper-V : Nano-Manager-1, Nano-Worker-1, Nano-Worker-2, etc...) 218 | 219 | ContainerHostName = "Nano-" 220 | 221 | Total Number of your Cluster Swarm Members (total of VMs in Hyper-V) : 222 | 223 | ServersInCluster = 3 224 | 225 | Set firewall rules for all our Cluster : Docker daemon, swarm, consul, veualt, registry, file sharing, winrm, etc ... 226 | if $True = set each FW rule (more than 16 rules .... secured ;o) !) 227 | if $False = we disable the Microsoft Firewall so all the ports are open inbound/outbound ... not secured ! 228 | 229 | $Firewall = $True 230 | 231 | Enabled Docker Daemon TLS ? Default $True, of course .... 232 | 233 | $global:EnabledDockerDaemonTLS = $True 234 | 235 | All the Docker Engine hosts (client, swarm manager(s) and swarm workers) have a copy of the CA’s certificate as well as their own key-pair signed by the CA. 236 | 237 | Our dedicated TCP Port for our Cluster Swarm Service : 238 | 239 | SwarmClusterPort = "2017" 240 | 241 | 242 | SwarmMSKit - Usage 243 | ----- 244 | 245 | ExecutionPolicy 246 | 247 | * Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser -Force 248 | or 249 | * Set-ExecutionPolicy Unrestricted 250 | 251 | Connect to the new NanoServer VM with WinRM protocol : 252 | 253 | * Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force 254 | * Restart-Service winrm 255 | 256 | Then run the SwarmMSKitProvisioning.ps1 file ... 257 | 258 | When it's finish ... 259 | 260 | * set DOCKER_HOST=tcp://@YourFirstIP:@YourClusterSwarmPort 261 | 262 | Examples : 263 | 264 | set DOCKER_HOST=tcp://10.1.0.24:2017 265 | docker images 266 | docker run -it nanoserver powershell 267 | docker run -it nanoserver cmd 268 | docker login -u admin -p admin123 @YourFirstIP:8123 269 | docker push nanoserver 270 | 271 | Browse your Private Registery (Host on Nexus OSS) : 272 | 273 | http://10.1.0.24:8081 274 | login: admin 275 | password: admin123 276 | 277 | Browse the Mangement UI Web Administration : 278 | 279 | http://10.1.0.24:9000 280 | login: admin 281 | password : you have to define your password the first time you connect 282 | 283 | A wonderfull open source Swarm Management UI : http://portainer.io/ 284 | 285 | SwarmMSKit - Usage with TLS Authentication 286 | ----- 287 | 288 | The SwarmMSKit tool will automatically configure your Docker Swarm for TLS per default. 289 | The client certificates are automatically generated in this folder : 290 | 291 | * $env:USERPROFILE\\.SwarmMSKit 292 | * %USERPROFILE%\\.SwarmMSKit 293 | 294 | Create a batch file (env.cmd) it with these environement variables like this : 295 | 296 | @echo off 297 | set DOCKER_TLS_VERIFY=1 298 | set DOCKER_CERT_PATH=%USERPROFILE%\.SwarmMSKit 299 | set DOCKER_HOST=tcp://10.1.0.24:2017 300 | 301 | In this example, the swarm manager have the @IP:10.1.0.24 and the swarm cluster port ist 2017 302 | 303 | Then execute it (cmd.exe) : 304 | 305 | * cd %USERPROFILE%\\.SwarmMSKit 306 | * %USERPROFILE%\\.SwarmMSKit\env.cmd 307 | 308 | You are now authenticate in the Cluster swarm and you can execute Docker commands : 309 | 310 | docker info 311 | docker run -it nanoserver powershell 312 | docker run -it nanoserver cmd 313 | 314 | 315 | SwarmMSKit - Youtube channel 316 | ----- 317 | 318 | https://youtu.be/HknSzK_djwo 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /SwarmMSKitProvisioning.ps1: -------------------------------------------------------------------------------- 1 | Set-StrictMode -version 3 2 | $ErrorActionPreference = "Stop" 3 | 4 | <# SwarmMSKit v1.0.0.3 5 | 6 | Author : Fouzi BOUKEZZOULA 7 | 8 | Twitter, Facebook : @fboukezzoula 9 | 10 | https://github.com/fboukezzoula/SwarmMSKit 11 | 12 | @March 2017 13 | 14 | -------------------------------------------------------------------------------------------------------------------------------------------------- 15 | 16 | Execution PS Security has to be set like for example : 17 | Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser -Force 18 | or 19 | Set-ExecutionPolicy Unrestricted 20 | 21 | For having a good output console for the special (French) characters, execute these commands on the PS Prompt before executing this ps1 programm 22 | $ConsoleCommand ="[Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding(437)" 23 | Invoke-Expression $ConsoleCommand 24 | 25 | To connect to our new NanoServer VM with WinRM protocol : 26 | 27 | Set-Item WSMan:\localhost\Client\TrustedHosts -Value "*" -Force 28 | Restart-Service winrm 29 | 30 | #> 31 | 32 | [System.Text.Encoding]::GetEncoding(437) | Out-Null 33 | 34 | #region Constants/Varibales 35 | ### ----------------------------------- 36 | ### Constants/Varibales 37 | ### ----------------------------------- 38 | 39 | $global:WorkDir = "C:\SwarmMSKit" 40 | $global:VMPath = "$global:WorkDir\VHD_Files" 41 | $global:Model_NanoServerDataCenter = "$global:WorkDir\VHD_Reference\NanoServerDataCenter.vhd" 42 | 43 | $global:LogDismTimestamp = (Get-Date -Format u).Replace("-","_").Replace(" ","_").Replace(":","").Replace("Z","") 44 | 45 | <# 46 | We have to create an virtual internal network switch on Hyper-V. The name of this Virtual Switch is InternalNetwork in this case 47 | We have set a static IP on this virtual NIC use for this internal network 48 | The DNS and AD will be use this static @IP and this virtual vlan 49 | 50 | Then we set the desire >> first @IP << of our Nanoserver VM of this subnet; which will be the first VM of our Cluster Swarm 51 | In this case, the first NanoServer VM of our Cluster Swarm will have this @IP 10.1.0.24 52 | #> 53 | 54 | $global:IPAddress = "10.1.0.24" 55 | $global:GatewayAddress = "10.1.0.1" 56 | $global:SubnetMask = "255.255.255.0" 57 | $global:DNSAddresses = "10.1.0.1" 58 | 59 | $global:Subnet = "10.1.0." 60 | $global:VMSwitch = "InternalNetwork" 61 | 62 | # System Configuration for our NanoServer VM, 4 vCPU and 2 Go Ram 63 | $global:VMProcessor = 4 64 | $global:VMRam = 2048MB 65 | 66 | # Type Of Authentication to the NanoServer : Local Account (default value: Local) or AD Account (AD) ? 67 | $global:AuthenticationType = "AD" 68 | 69 | # Prefix of your VM Nanoserver in Hyper-V and for the hostname, if "Nano-" > Nano-Manager-01, Nano-Worker-01, etc .... 70 | $global:ContainerHostName = "Nano-" 71 | 72 | #How many Members/VM NanoServer will be deploy in your Cluster Swarm ? 73 | $global:ServersInCluster = 3 74 | 75 | # Our dedicated TCP Port for our Cluster Swarm Service 76 | $global:SwarmClusterPort = "2017" 77 | 78 | # Set firewall rules for all our Cluster : Docker daemon, swarm, consul, veualt, registry, file sharing, winrm, etc ... 79 | # if $True = set each FW rule 80 | # if $False = we disable the Microsoft Firewall so all the ports ar open inbound/outbound 81 | $Firewall = $True 82 | 83 | # Enabled Docker Daemon TLS ? Default $True, of course .... 84 | $global:EnabledDockerDaemonTLS = $True 85 | 86 | $LastOctetAdress = $IPAddress.Split('.') 87 | $NextIP = [int]($LastOctetAdress[-1]) 88 | 89 | $global:InterfaceNameOrIndex = (Get-NetAdapter | Where-Object {$_.Name -eq "vEthernet ($global:VMSwitch)"}).ifIndex 90 | 91 | $global:ClusterMembers = [System.Collections.ArrayList]@("$IPAddress") 92 | $global:ContainerIPAdress = [System.Collections.ArrayList]@("") 93 | #endregion 94 | 95 | 96 | Import-Module $WorkDir\SwarmMSKit -Force -Verbose 97 | 98 | #region ServersConfigurationCluster 99 | Function global:ServersConfigurationCluster { 100 | 101 | param ($NbrSwarmManager,$NbrConsulServer,$NbrSwarmNode,$NbrConsulAgent) 102 | 103 | if ($NbrSwarmManager -gt 1) { 104 | $NbreSwarmManagerNodes ="* $NbrSwarmManager Swarm Managers - $NbrConsulServer Consul Servers *" 105 | } elseif ($NbrSwarmManager -eq 1) { 106 | $NbreSwarmManagerNodes ="* $NbrSwarmManager Swarm Manager - $NbrConsulServer Consul Server *" 107 | } 108 | 109 | $myCluster = @" 110 | 111 | ******************************************************************************************* 112 | * The configuration of your Cluster Swarm will be : * 113 | * * 114 | * * 115 | $NbreSwarmManagerNodes 116 | * $NbrSwarmNode Swarm Workers - $NbrConsulAgent Consul Agents * 117 | * * 118 | * * 119 | * And 1 Vault Server for all your Cluster Swarm * 120 | ******************************************************************************************* 121 | 122 | "@ 123 | 124 | $myCluster 125 | 126 | } 127 | #endregion 128 | 129 | #region ClusterNodeCreation 130 | Function global:ClusterNodeCreation { 131 | 132 | param([bool]$IsSwarmManager,[String]$ContainerHostNameNodeCreate, [String]$ContainerIPNodeCreate, [String]$BootstrapExpectServers) 133 | 134 | $global:ContainerIPAdress[0]="$ContainerIPNodeCreate" 135 | 136 | $ClusterMembers.Add("$ContainerIPNodeCreate") | Out-Null 137 | 138 | if(Test-Path -Path $global:VMPath\$ContainerHostNameNodeCreate.vhd) { 139 | Remove-Item -Path $global:VMPath\$ContainerHostNameNodeCreate.vhd -Force 140 | } 141 | 142 | "Cloning the VHD NanoServerDataCenter reference built image and renaming the VHD image with the target NanoServer Name : $ContainerHostNameNodeCreate.vhd ... Please Wait ..." 143 | Copy-Item -Path $global:Model_NanoServerDataCenter -Destination $global:VMPath\$ContainerHostNameNodeCreate.vhd 144 | " " 145 | PurgeTempMountFolder "$global:VMPath\nanoserver-offine-temp" 146 | PurgeTempMountFolder "$global:VMPath\Logs" 147 | 148 | New-Item -Type Directory -Path "$global:VMPath\nanoserver-offine-temp" -Force | Out-Null 149 | New-Item -Type Directory -Path "$global:VMPath\Logs" -Force | Out-Null 150 | New-Item -Type Directory -Path "$global:VMPath\Logs\$global:LogDismTimestamp" -Force | Out-Null 151 | 152 | $MountImage="Dism /Mount-Image /ImageFile:$global:VMPath\$ContainerHostNameNodeCreate.vhd /index:1 /MountDir:$global:VMPath\nanoserver-offine-temp /LogLevel:3 /LogPath:$global:VMPath\Logs\$global:LogDismTimestamp\dism.log /Quiet" 153 | $MountImage 154 | " " 155 | Invoke-Expression $MountImage | Out-Host | Out-Null 156 | 157 | if ($AuthenticationType -eq "Local") { 158 | 159 | "Using a Local account ... Injection this credential to the target NanoServer Name : $ContainerHostNameNodeCreate.vhd ... Please Wait ... " 160 | " " 161 | # Local Credential which will be the admin NanoServer account 162 | $global:Username = "Administrator" 163 | $global:clearadminPassword = "ertyui" 164 | 165 | $global:adminPassword = ConvertTo-SecureString $clearadminPassword -AsPlainText -Force 166 | $global:Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$adminPassword 167 | 168 | $unattend = @" 169 | 170 | 171 | 172 | 173 | 174 | $ContainerHostNameNodeCreate 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | $global:clearadminPassword 183 | true</PlainText> 184 | </AdministratorPassword> 185 | </UserAccounts> 186 | 187 | </component> 188 | </settings> 189 | 190 | <settings pass="specialize"> 191 | <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"> 192 | <RegisteredOwner>Fouzi BOUKEZZOULA</RegisteredOwner> 193 | <RegisteredOrganization>SwarmMSKit</RegisteredOrganization> 194 | </component> 195 | </settings> 196 | </unattend> 197 | "@ 198 | 199 | $unattend > "$WorkDir\unattend.xml" 200 | 201 | (Get-Content -path "$WorkDir\unattend.xml" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$WorkDir\unattend.xml" 202 | 203 | New-Item -Type Directory -Path "$global:VMPath\nanoserver-offine-temp\Windows\panther" -Force | Out-Null 204 | 205 | $Command_Inject_unattend = "dism.exe /Image:'$global:VMPath\nanoserver-offine-temp' /Apply-Unattend:'$WorkDir\unattend.xml'" 206 | $Command_Inject_unattend 207 | 208 | Invoke-Expression $Command_Inject_unattend 209 | 210 | Copy-Item $WorkDir\unattend.xml $global:VMPath\nanoserver-offine-temp\Windows\panther 211 | 212 | 213 | } else { 214 | 215 | "Using a Domain Active Directory account ... Injection this credential to the target NanoServer Name : $ContainerHostNameNodeCreate.vhd ... Please Wait ..." 216 | " " 217 | 218 | # AD Credential which will be the admin NanoServer account 219 | $global:Username = "FBOUKEZZOULA\Administrateur" 220 | $global:clearadminPassword = "change with your login and P@swwaord AD" 221 | $global:DomainName = "FBOUKEZZOULA" 222 | $global:adminPassword = ConvertTo-SecureString $clearadminPassword -AsPlainText -Force 223 | 224 | $global:Cred = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$adminPassword 225 | 226 | 227 | $global:DomainBlobPath = "$WorkDir\DJOIN_$ContainerHostNameNodeCreate.TXT" 228 | # we integrated/join automatically the NanoServer VM in an Active Directory Domain with the DJOIN command and use the blob file during the Nanoserver installation 229 | $Command_Join = "DJOIN /provision /domain $DomainName /machine $ContainerHostNameNodeCreate /REUSE /savefile $DomainBlobPath" 230 | $Command_Join 231 | " " 232 | Invoke-Expression $Command_Join 233 | 234 | $Command_Inject_Join = "djoin /RequestODJ /LoadFile '$DomainBlobPath' /WindowsPath '$global:VMPath\nanoserver-offine-temp\Windows' 2>&1" 235 | $Command_Inject_Join 236 | " " 237 | 238 | Invoke-Expression $Command_Inject_Join 239 | 240 | } 241 | 242 | 243 | 244 | if ($EnabledDockerDaemonTLS -eq "$True") { 245 | 246 | DockerSwarmMSKitWithTLSAuthentication $global:ServersInCluster $ContainerIPNodeCreate $ClusterMembers[0] $global:Subnet 247 | 248 | } 249 | 250 | " " 251 | $CommitImage="Dism /Commit-Image /MountDir:$global:VMPath\nanoserver-offine-temp /LogLevel:3 /LogPath:$global:VMPath\Logs\$global:LogDismTimestamp\dism.log /Quiet" 252 | $CommitImage 253 | " " 254 | Invoke-Expression $CommitImage | Out-Host | Out-Null 255 | 256 | $UnmountImage="Dism /Unmount-Image /MountDir:$global:VMPath\nanoserver-offine-temp /commit /LogLevel:3 /LogPath:$global:VMPath\Logs\$global:LogDismTimestamp\dism.log /Quiet" 257 | $UnmountImage 258 | " " 259 | Invoke-Expression $UnmountImage | Out-Host | Out-Null 260 | 261 | 262 | "Create, Set vCPU and RAM, Enabled the VM according your configuration and start the VM Name $ContainerHostNameNodeCreate in the Hyper-V ... Please Wait ..." 263 | " " 264 | Create-Set-VM-Hyper-V $global:VMSwitch $global:VMPath $ContainerHostNameNodeCreate $global:VMProcessor $global:VMRam | Out-Host | Out-Null 265 | 266 | "Get-VMNetworkAdapter -VMName $ContainerHostNameNodeCreate | Set-VMNetworkConfiguration -IPAddress $ContainerIPNodeCreate -Subnet $SubnetMask -DNSServer $DNSAddresses -DefaultGateway $GatewayAddress" 267 | Get-VMNetworkAdapter -VMName $ContainerHostNameNodeCreate | Set-VMNetworkConfiguration -IPAddress $ContainerIPNodeCreate -Subnet $SubnetMask -DNSServer $DNSAddresses -DefaultGateway $GatewayAddress 268 | Enabled-VM-Hyper-V $ContainerHostNameNodeCreate | Out-Host | Out-Null 269 | 270 | Start-VM -Name $ContainerHostNameNodeCreate 271 | 272 | Pause 30 $ContainerHostNameNodeCreate | Out-Host | Out-Null 273 | 274 | Wait-WinRM-Reachable $ContainerIPNodeCreate 275 | 276 | # Prepare the VM NanoServer: with ou without Firewall, Regional Settings, etc ... if first Argument = $True, we set all the FW rules; if $False, we disable the FW MS service (no secured) 277 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:NanoSetup} -ArgumentList $global:Firewall, $global:SwarmClusterPort | Out-Host | Out-Null 278 | 279 | Pause 30 $ContainerHostNameNodeCreate | Out-Host | Out-Null 280 | 281 | Wait-WinRM-Reachable $ContainerIPNodeCreate 282 | 283 | 284 | if ($IsSwarmManager -eq "$True") { 285 | 286 | # If Swarm Manager/Consul Server Node 287 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:DockerInstallation} -ArgumentList $True, $global:EnabledDockerDaemonTLS, $ClusterMembers[0], $ContainerIPNodeCreate, $global:Username, $global:clearadminPassword | Out-Host | Out-Null 288 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:ConsulServer} -ArgumentList $ContainerIPNodeCreate, $ClusterMembers[0], $BootstrapExpectServers | Out-Host | Out-Null 289 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:SwarmManager} -ArgumentList $ContainerIPNodeCreate, $global:EnabledDockerDaemonTLS, $ClusterMembers[0], $global:SwarmClusterPort | Out-Host | Out-Null 290 | 291 | } 292 | 293 | else { 294 | 295 | # If Swarm Node/Consul Agent Node : 296 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:DockerInstallation} -ArgumentList $False, $global:EnabledDockerDaemonTLS, $ClusterMembers[0],$ContainerIPNodeCreate, $global:Username, $global:clearadminPassword | Out-Host | Out-Null 297 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:ConsulAgent} -ArgumentList $ContainerIPNodeCreate, $ClusterMembers[0] | Out-Host | Out-Null 298 | Invoke-Command -ComputerName $ContainerIPNodeCreate -Credential $global:Cred -ScriptBlock ${function:SwarmWorker} -ArgumentList $ContainerIPNodeCreate, $global:EnabledDockerDaemonTLS, $ClusterMembers[0], $global:SwarmClusterPort | Out-Host | Out-Null 299 | 300 | } 301 | 302 | 303 | } 304 | #endregion 305 | 306 | #region CreateServerNode 307 | Function global:CreateServerNode { 308 | 309 | param($ServersInCluster,$ContainerHostName,$NextIP,$NbrSwarmManager,$NbrSwarmNode) 310 | 311 | $TotalServers = 1 312 | 313 | Do 314 | { 315 | 316 | ### Create the SwarmManager Node(s) and Consul Server(s) Node(s) 317 | $TotalSwarmManager = 1 318 | 319 | Do 320 | { 321 | $NextIP = [int]($LastOctetAdress[-1]) + $TotalSwarmManager 322 | $Base = $NextIP-1 323 | 324 | $IPAdressClusterSwarmManager = "$Subnet$Base" 325 | 326 | " " 327 | "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 328 | $Message = "Starting Creation SwarmManager/Consul Server : "+$ContainerHostName+"Manager-0"+$TotalSwarmManager+ " [@IP :" +$IPAdressClusterSwarmManager +"]" 329 | $Message 330 | "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 331 | " " 332 | 333 | $ContainerHostNameNodeCreate = $ContainerHostName+"Manager-0"+$TotalSwarmManager 334 | 335 | ClusterNodeCreation $True $ContainerHostNameNodeCreate $IPAdressClusterSwarmManager $NbrSwarmManager 336 | 337 | $TotalSwarmManager++ 338 | $TotalServers++ 339 | 340 | } While ($TotalSwarmManager -le $NbrSwarmManager) 341 | 342 | ### Create the SwarwWorker(s) and Consul Agents(s) Node(s) 343 | $TotalSwarmNodes = 1 344 | Do 345 | { 346 | $NextIP = [int]($LastOctetAdress[-1]) + $TotalServers 347 | $Base = $NextIP-1 348 | $IPAdressClusterSwarmNode = "$Subnet$Base" 349 | 350 | " " 351 | "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 352 | $Message = "Starting Creation SwarmWorker/Consul Agent : " +$ContainerHostName+"Worker-0"+$TotalSwarmNodes+ " [@IP :" +$IPAdressClusterSwarmNode +"]" 353 | $Message 354 | "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" 355 | " " 356 | 357 | $ContainerHostNameNodeCreate = $ContainerHostName+"Worker-0"+$TotalSwarmNodes 358 | 359 | ClusterNodeCreation $False $ContainerHostNameNodeCreate $IPAdressClusterSwarmNode $NbrSwarmManager 360 | 361 | $TotalSwarmNodes++ 362 | $TotalServers++ 363 | 364 | } While ($TotalSwarmNodes -le $NbrSwarmNode) 365 | 366 | } while ($TotalServers -le $ServersInCluster) 367 | 368 | } 369 | #endregion 370 | 371 | 372 | ## MAIN PROGRAMM 373 | Clear-Host; 374 | 375 | Write-Host "Start at : $(Get-Date -format t)" 376 | $Start = (Get-Date).Minute 377 | " " 378 | 379 | if ($ServersInCluster -lt 3) { 380 | Write-Host " ";Write-host "For creating a cluster Swarm with this programm, you need miminum 3 VMs. Please choose a correct number of Servers and retry ..." ; 381 | Write-Host " "; break 382 | } 383 | # If ServersInCluster (>= 3 and < 5) (=3,4) 384 | elseif (($ServersInCluster -ge 3) -and ($ServersInCluster -lt 5)) { 385 | 386 | $NbrSwarmManager = $NbrConsulServer = 1 387 | $NbrSwarmNode = $NbrConsulAgent = ([int]($ServersInCluster)-1) 388 | } 389 | # If ServersInCluster (>= 5 and < 8) 390 | elseif (($ServersInCluster -ge 5) -and ($ServersInCluster -lt 8)) { 391 | 392 | $NbrSwarmManager = $NbrConsulServer = 2 393 | $NbrSwarmNode = $NbrConsulAgent = ([int]($ServersInCluster)-2) 394 | } 395 | # If ServersInCluster (>= 8) 396 | elseif ($ServersInCluster -ge 8) { 397 | 398 | $NbrSwarmManager = $NbrConsulServer = 3 399 | $NbrSwarmNode = $NbrConsulAgent = ([int]($ServersInCluster)-3) 400 | } 401 | 402 | # Let's Go ! 403 | 404 | ServersConfigurationCluster $NbrSwarmManager $NbrConsulServer $NbrSwarmNode $NbrConsulAgent 405 | 406 | CreateServerNode $ServersInCluster $ContainerHostName $NextIP $NbrSwarmManager $NbrSwarmNode | Out-Host | Out-Null 407 | 408 | Invoke-Command -ComputerName $ClusterMembers[0] -Credential $global:Cred -ScriptBlock ${function:VaultServer-PrivateRegistry-UCP} -ArgumentList $ClusterMembers[0], $global:SwarmClusterPort, $global:EnabledDockerDaemonTLS | Out-Host | Out-Null 409 | 410 | # Finally, after building the portainer.cmd we launch the UCP IHM/GUI on the first member of the cluster which is a swarm manager, a consul server and a vault server 411 | $PSCommand = "c:\portainer\portainer.cmd" 412 | Invoke-Command -ScriptBlock {param($command) cmd /c $command} -args $PSCommand -ComputerName $ClusterMembers[0] -Credential $global:Cred -AsJob 413 | 414 | SwarMSKit-Check $ClusterMembers[0] $global:SwarmClusterPort $global:EnabledDockerDaemonTLS $global:Username $global:clearadminPassword 415 | 416 | " " 417 | " " 418 | Write-Host "End at : $(Get-Date -format t)" 419 | $End = (Get-Date).Minute 420 | 421 | " " 422 | " " 423 | " " 424 | "**********************************************************************************************************" 425 | " " 426 | " " 427 | " Full Processing time for deploying your Cluster Swarm with all dependencies and tools :" 428 | " " 429 | " " 430 | " $($End-$Start) Minutes" 431 | 432 | 433 | -------------------------------------------------------------------------------- /SwarmMSKit/SwarmMSKit.psm1: -------------------------------------------------------------------------------- 1 | # Purge the Temp folder of the mount image if exist for the next loop process 2 | Function global:PurgeTempMountFolder { 3 | 4 | param([String]$TempMountFolder) 5 | 6 | if ((Test-Path -Path $TempMountFolder)) { 7 | Remove-Item -Path $TempMountFolder -Recurse -Force | Out-Null 8 | } 9 | } 10 | 11 | # Simple pause function for waiting the VM booting or waiting the tools installation, etc ... 12 | Function global:Pause { 13 | 14 | param([int]$Seconds,[String]$ContainerHostNameNodeCreate) 15 | 16 | " " 17 | "Wait while NanoServer VM $ContainerHostNameNodeCreate is performing post-installation tasks and for booting ..." 18 | " " 19 | Start-Sleep -Seconds $Seconds 20 | } 21 | 22 | # Create and configure a new VM host in Hyper-V 23 | Function global:Create-Set-VM-Hyper-V { 24 | 25 | param([String]$VMSwitch, 26 | [String]$VMPath, 27 | [String]$VMName, 28 | [Int]$VMProcessor, 29 | [String]$VMRam) 30 | 31 | New-VM -Name "$VMName" -MemoryStartupBytes $VMRam -SwitchName "$VMSwitch" -VHDPath "$VMPath\$VMName.vhd" -Path "$VMPath" -Generation 1 32 | 33 | Set-VMMemory $VMName -DynamicMemoryEnabled $false 34 | 35 | Set-VMProcessor -VMName $VMName -Count $VMProcessor -Reserve 10 -Maximum 75 -RelativeWeight 200 -ExposeVirtualizationExtensions $true 36 | 37 | Get-VMNetworkAdapter -VMName $VMName | Set-VMNetworkAdapter -MacAddressSpoofing On 38 | 39 | } 40 | 41 | # Enable and start a created VM host on Hyper-V 42 | Function global:Enabled-VM-Hyper-V { 43 | 44 | param([String]$VMName) 45 | 46 | $vm = get-vm 47 | 48 | Foreach($v in $vm) 49 | 50 | { 51 | 52 | Get-VMIntegrationService -VM $v | 53 | 54 | Foreach-object { 55 | 56 | if(!($_.enabled)) 57 | 58 | { 59 | Enable-VMIntegrationService -Name $_.name -VM $v 60 | } 61 | } 62 | 63 | } 64 | } 65 | 66 | # Telnet WinRM service (TCP Port: 5985) on the new NanoServer VM to be sure that it was correctly reboot and online before performing several tasks (installation docker, swarm, consul, vault, nexus3, etc ...) 67 | Function global:Wait-WinRM-Reachable { 68 | 69 | param([String]$ContainerIPNodeCreate) 70 | 71 | Do 72 | { 73 | #$a = Test-NetConnection ((Get-VMNetworkAdapter -VMName $ContainerHostName).IpAddresses | where { $_ -match "\." }) -Port 5985 -ErrorAction SilentlyContinue 74 | $a = Test-NetConnection $ContainerIPNodeCreate -Port 5985 -ErrorAction SilentlyContinue 75 | $result = $a.TcpTestSucceeded 76 | 77 | } While ($result -ne "True") 78 | 79 | } 80 | 81 | # function to install and configure the Docker Daemon on each new NanoServer VM : we don't forget to use set in the daemon.json configuration file the @IP of our consul registry discovery service, private registry, etc ... 82 | Function global:DockerInstallation { 83 | 84 | param([bool]$SwarmManager,[bool]$EnabledDockerDaemonTLS,[String]$IPConsulMaster,[String]$ContainerIPNodeCreate,[String]$Username,[String]$clearadminPassword) 85 | 86 | setx path "%PATH%;$env:ProgramFiles\docker\" 87 | 88 | # To prevent an error is thrown indicating a timeout event, the following PowerShell command fix it 89 | Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 0 -Force 90 | 91 | Stop-Service Docker 92 | 93 | $consul = $IPConsulMaster + ":8500" 94 | 95 | $daemon_json_file = "c:\ProgramData\docker\config\daemon.json" 96 | 97 | if ($EnabledDockerDaemonTLS -eq "$True") { 98 | 99 | $advertise = $IPConsulMaster + ":2376" 100 | 101 | $json = @" 102 | { 103 | "tlscacert": "C:\\ProgramData\\docker\\certs.d\\ca.pem", 104 | "tlsverify": true, 105 | "hosts": [ 106 | "tcp://0.0.0.0:2376", 107 | "npipe://" 108 | ], 109 | "tlscert": "C:\\ProgramData\\docker\\certs.d\\cert.pem", 110 | "tlskey": "C:\\ProgramData\\docker\\certs.d\\key.pem", 111 | "insecure-registries": ["$IPConsulMaster`:8123"] 112 | } 113 | "@ 114 | $Source = "\\$IPConsulMaster\c$\ProgramData\OpenSSL\TLS" 115 | $Dest = "C:\ProgramData\docker\certs.d" 116 | 117 | New-Item -Type Directory -Path $Dest -Force | Out-Null 118 | 119 | $MapDrive ="net use S: $Source /user:$Username $clearadminPassword" 120 | Invoke-Expression $MapDrive 121 | 122 | Copy-Item -Path S:\ca.pem -Destination $Dest\ca.pem 123 | Copy-Item -Path S:\$ContainerIPNodeCreate-cert.pem -Destination $Dest\cert.pem 124 | Copy-Item -Path S:\$ContainerIPNodeCreate-priv-key.pem -Destination $Dest\key.pem 125 | 126 | } else { 127 | 128 | $advertise = $IPConsulMaster + ":2375" 129 | 130 | $json = @" 131 | { 132 | "hosts": ["tcp://0.0.0.0:2375","npipe://"], 133 | "insecure-registries": ["$IPConsulMaster`:8123"] 134 | } 135 | "@ 136 | 137 | } 138 | $jobj = ConvertFrom-Json -InputObject $json 139 | 140 | if ($SwarmManager -eq "$True") { 141 | 142 | $jobj | add-member "cluster-store" "consul://$consul" -MemberType NoteProperty 143 | $jobj | add-member "cluster-advertise" "$advertise" -MemberType NoteProperty 144 | 145 | } 146 | 147 | ConvertTo-Json $jobj | Out-File $daemon_json_file 148 | 149 | (Get-Content -path "$daemon_json_file" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$daemon_json_file" 150 | 151 | if (Test-Path $env:ProgramData\docker.pid) {Remove-Item $env:ProgramData\docker.pid -Force} 152 | Set-Service -name Docker -startupType Disabled 153 | 154 | # we install the Docker Daemon as an Windows Service with nssm 155 | nssm install SwarmMSKit-DockerDaemon dockerd.exe | Out-Null 156 | nssm set SwarmMSKit-DockerDaemon Description "SwarmMSKit-DockerDaemon Docker Daemon Service" | Out-Null 157 | nssm set SwarmMSKit-DockerDaemon AppStdout C:\Logs\dockerdaemon\service.log | Out-Null 158 | nssm set SwarmMSKit-DockerDaemon AppStderr C:\Logs\dockerdaemon\service.log | Out-Null 159 | nssm start SwarmMSKit-DockerDaemon | Out-Null 160 | 161 | " " 162 | docker --version 163 | " " 164 | docker info 165 | " " 166 | docker images 167 | " " 168 | } 169 | 170 | # function to make several installations : 171 | # install and configure the Vault Server Secret Management Store on the first created NanoServer VM of the cluster swarm (which is an Consul Server and an Swarm Manager too) 172 | # install and configure a dedicated private registry for hosting our docker images. We use the last version of Nexus OSS which is free 173 | # install and configure a IHM/GUI like DDC UCP (portainer) 174 | 175 | Function global:VaultServer-PrivateRegistry-UCP { 176 | 177 | param([String]$IPVaultServer,[int]$SwarmClusterPort,[bool]$EnabledDockerDaemonTLS) 178 | 179 | $DestinationFolder_hcl_configuration_file = "c:\vault\vault-fboukezzoula.hcl" 180 | $DestinationFolder_vault_keys_file = "c:\vault\vaul_init.txt" 181 | $DestinationFolder_UnsealVaultScript = "c:\vault\unseal_vault.cmd" 182 | 183 | New-Item -Type Directory -Path "c:\vault" -Force | Out-Null 184 | 185 | # Vault hcl/json configuration file, notice that you can use a TLS connection. 186 | $hcl_configuration_file = @" 187 | 188 | disable_mlock = true 189 | default_lease_ttl = "24h" 190 | max_lease_ttl = "24h" 191 | 192 | backend "consul" { 193 | address = "$IPVaultServer`:8500" 194 | path = "vault" 195 | } 196 | 197 | listener "tcp" { 198 | address = "$IPVaultServer`:8200" 199 | tls_disable = 1 200 | 201 | } 202 | 203 | "@ 204 | 205 | $hcl_configuration_file | Out-File $DestinationFolder_hcl_configuration_file 206 | 207 | (Get-Content -path "$DestinationFolder_hcl_configuration_file" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$DestinationFolder_hcl_configuration_file" 208 | 209 | " " 210 | "Installation and configuration of the VAULT Server ... Please wait ..." 211 | " " 212 | 213 | # we install the Vault Server as an Windows Service with nssm 214 | nssm install SwarmMSKit-VaultServer vault server -config="$DestinationFolder_hcl_configuration_file" | Out-Null 215 | nssm set SwarmMSKit-VaultServer Description "Securely managing secrets and encrypting data in-transit for our Cluster Swarm NanoServer Windows" | Out-Null 216 | nssm set SwarmMSKit-VaultServer AppStdout C:\Logs\vault-consul\service-vault.log | Out-Null 217 | nssm set SwarmMSKit-VaultServer AppStderr C:\Logs\vault-consul\service-vault.log | Out-Null 218 | nssm start SwarmMSKit-VaultServer | Out-Null 219 | " " 220 | 221 | # Initialisation the Vault Secret Management Store and write in a file the root token and all the keys for unsealing 222 | 223 | $CommandInitVault="vault init -address=http://" + $IPVaultServer + ":8200 > $DestinationFolder_vault_keys_file" 224 | Invoke-Expression $CommandInitVault 225 | 226 | # UnSealed the Vault Secret Management Store; so we can create keys and browse the store with the consul UI 227 | 228 | $UnsealKeys = gc $DestinationFolder_vault_keys_file 229 | 230 | $AllUnsKeyseal = $UnsealKeys.split(' ',4) 231 | 232 | foreach ($key in $AllUnsKeyseal[3,7,11,15,19]) { 233 | 234 | " " 235 | "vault unseal $key" 236 | $CommandInitVault = "vault unseal -address=http://" + $IPVaultServer + ":8200 $key" 237 | Invoke-Expression $CommandInitVault 238 | " " 239 | } 240 | 241 | # Create a Powershell script and create a schedule task to execute this PowerShell script each time the VM NanoServer starts 242 | # This task it's for unsealing the Vault Store with the Unseal Keys create in the initi process 243 | 244 | foreach ($key in $AllUnsKeyseal[3,7,11,15,19]) { 245 | 246 | $CommandInitVault = "vault unseal -address=http://" + $IPVaultServer + ":8200 $key" ; Add-Content $DestinationFolder_UnsealVaultScript $CommandInitVault 247 | } 248 | 249 | $CreateSchedukeTask = "schtasks /create /tn 'Unseal Vault onStart' /tr $DestinationFolder_UnsealVaultScript /sc onstart /ru 'System'" 250 | Invoke-Expression $CreateSchedukeTask 251 | 252 | 253 | # INSTALLATION PRIVATE REGISTRY - SwarmMSKitPrivateRegistry 254 | 255 | $NexusArchive = "c:\tools\nexus-3.2.0-01-win64.zip" 256 | 257 | Expand-Archive -Path $NexusArchive -DestinationPath c:\PrivateRegistry -Force | Out-Null 258 | 259 | $InstallServiceSwarmMSKitPrivateRegistry = "c:\PrivateRegistry\nexus-3.2.0-01\bin\nexus.exe /install SwarmMSKit-PrivateRegistry" 260 | $StartServiceSwarmMSKitPrivateRegistry = "c:\PrivateRegistry\nexus-3.2.0-01\bin\nexus.exe /start SwarmMSKit-PrivateRegistry" 261 | 262 | " " 263 | "Install the SwarmMSKit-Private-Registry service to store our Docker images ..." 264 | " " 265 | 266 | Invoke-Expression $InstallServiceSwarmMSKitPrivateRegistry | Out-Host | Out-Null 267 | Invoke-Expression $StartServiceSwarmMSKitPrivateRegistry | Out-Host | Out-Null 268 | 269 | # INSTALLATION SwarmMSKit-UCP 270 | 271 | $PortainerArchive = "c:\tools\portainer.zip" 272 | 273 | " " 274 | "Installation and configuration of the Web Admin Cluster Swarm - Portainer (Like UCP of Docke Inc) ... Please wait ..." 275 | " " 276 | 277 | Expand-Archive -Path $PortainerArchive -DestinationPath c:\portainer -Force | Out-Null 278 | 279 | $DestinationFolder_SwarmMSKit_UCP = "c:\portainer\portainer.cmd" 280 | 281 | $SWARM_HOST = "tcp://" + $IPVaultServer + ":$SwarmClusterPort" 282 | 283 | $TLSverify = "--tlsverify --tlscacert=C:\\ProgramData\\docker\\certs.d\\ca.pem --tlscert=C:\\ProgramData\\docker\\certs.d\\cert.pem --tlskey=C:\\ProgramData\\docker\\certs.d\\key.pem" 284 | 285 | # create the cmd file that will be run at the end - this bach launch the Portainer which is a simple but very cool management solution for Docker. 286 | $CommandLaunchSwarmMSKit_UCP ="set portainerdir=""c:\portainer""" ; Add-Content $DestinationFolder_SwarmMSKit_UCP $CommandLaunchSwarmMSKit_UCP 287 | $CommandLaunchSwarmMSKit_UCP ="cd /d %portainerdir%" ; Add-Content $DestinationFolder_SwarmMSKit_UCP $CommandLaunchSwarmMSKit_UCP 288 | 289 | if ($EnabledDockerDaemonTLS -eq "$True") { 290 | 291 | $CommandLaunchSwarmMSKit_UCP =".\portainer.exe -H $SWARM_HOST --logo ""http://$IPVaultServer/logoSwarmMSKit.png"" --templates http://$IPVaultServer/templates.json $TLSverify" ; Add-Content $DestinationFolder_SwarmMSKit_UCP $CommandLaunchSwarmMSKit_UCP 292 | 293 | } else { 294 | 295 | $CommandLaunchSwarmMSKit_UCP =".\portainer.exe -H $SWARM_HOST --logo ""http://$IPVaultServer/logoSwarmMSKit.png"" --templates http://$IPVaultServer/templates.json" ; Add-Content $DestinationFolder_SwarmMSKit_UCP $CommandLaunchSwarmMSKit_UCP 296 | 297 | } 298 | 299 | $ExecutePortainer = "c:\portainer\portainer.cmd" 300 | 301 | # create a scheduke task for onstart to run/launch the Portainer 302 | $CreateSchedukeTask = "schtasks /create /tn 'SwarmMSKit-UCP' /tr $ExecutePortainer /sc onstart /ru 'System'" 303 | 304 | # create the json template file for the app services in the IHM 305 | $template_json ="c:\inetpub\wwwroot\templates.json" 306 | $string = gc $template_json 307 | $string.replace("##PORTAINER_HOST##","$IPVaultServer") | out-file $template_json 308 | 309 | (Get-Content -path "$template_json" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$template_json" 310 | 311 | 312 | $DestinationFolder_DockerHostedRepository = "c:\PrivateRegistry\dockerhost.json" 313 | 314 | $DockerHostedRepository = @" 315 | { 316 | "name": "swarmmskit", 317 | "type" : "groovy", 318 | "v1Enabled" : "true", 319 | "strictContentTypeValidation" : "true", 320 | "content": "repository.createDockerHosted('swarmmskit',8123,4443)" 321 | } 322 | "@ 323 | 324 | $DockerHostedRepository| Out-File $DestinationFolder_DockerHostedRepository 325 | 326 | (Get-Content -path "$DestinationFolder_DockerHostedRepository" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$DestinationFolder_DockerHostedRepository" 327 | 328 | " " 329 | "Create a DockerHost Repository called 'swarmmskit' for our Docker images in the deployed ..." 330 | "Please wait while the Private Registry is running and wait for the Docker Hosted Repository has been created ...." 331 | " " 332 | 333 | $CreateDockerHostRepository = "curl -s -u admin:admin123 --header ""Content-Type: application/json"" ""http://$IPVaultServer"+":8081/service/siesta/rest/v1/script/"" -d @$DestinationFolder_DockerHostedRepository" 334 | $RunGroovyDockerHostRepository = "curl -s -X POST -u admin:admin123 --header ""Content-Type: text/plain"" ""http://$IPVaultServer"+":8081/service/siesta/rest/v1/script/swarmmskit/run""" 335 | 336 | " " 337 | $CreateDockerHostRepository 338 | " " 339 | $RunGroovyDockerHostRepository 340 | " " 341 | 342 | 343 | Start-Sleep -Seconds 75 344 | 345 | Do 346 | { 347 | $a = Test-NetConnection $IPVaultServer -Port 8081 -ErrorAction SilentlyContinue 348 | $result = $a.TcpTestSucceeded 349 | 350 | } While ($result -ne "True") 351 | 352 | Invoke-Expression $CreateDockerHostRepository -ErrorAction SilentlyContinue | Out-Host | Out-Null 353 | Invoke-Expression $RunGroovyDockerHostRepository -ErrorAction SilentlyContinue | Out-Host | Out-Null 354 | 355 | 356 | } 357 | 358 | # function to install and configure the Consul Server 359 | Function global:ConsulServer { 360 | 361 | param ([String]$IPVM,[String]$IPConsulServer,[int]$BootstrapExpectServers) 362 | 363 | #Write-Host "Number of Consul Server(s) in the cluster Swarm : " $BootstrapExpectServers 364 | 365 | # we install the Consul Server as an Windows Service with nssm 366 | " " 367 | nssm install SwarmMSKit-ConsulServer consul.exe "agent -server -data-dir C:\consul\database\ -ui-dir C:\consul\consul_web_ui\ -bootstrap-expect $BootstrapExpectServers -bind=""$IPVM"" -client=""$IPVM"" -dc=""nano-swarm""" | Out-Null 368 | nssm set SwarmMSKit-ConsulServer Description "Discovery Service and Key/Value Store use for our Cluster Swarm NanoServer Windows" | Out-Null 369 | nssm set SwarmMSKit-ConsulServer AppStdout C:\Logs\vault-consul\service-consul.log | Out-Null 370 | nssm set SwarmMSKit-ConsulServer AppStderr C:\Logs\vault-consul\service-consul.log | Out-Null 371 | nssm start SwarmMSKit-ConsulServer | Out-Null 372 | " " 373 | 374 | $ConsulServerReference = $IPConsulServer + ":8400" 375 | 376 | if ($BootstrapExpectServers -gt 1) { 377 | 378 | " " 379 | consul join -rpc-addr="$ConsulServerReference" $IPVM 380 | " " 381 | } 382 | 383 | } 384 | 385 | # function to install and configure the Consul Agent (on each Swarm Worker of our Cluster Swarm) 386 | Function global:ConsulAgent { 387 | 388 | param ([String]$IPVM,[String]$IPConsulServer) 389 | 390 | $ConsulServerReference = $IPConsulServer + ":8400" 391 | 392 | # we install the Consul Agent as an Windows Service with nssm 393 | " " 394 | nssm install SwarmMSKit-ConsulAgent consul.exe "agent -data-dir C:\consul\database\ -ui-dir C:\consul\ui\ -bind=""$IPVM"" -client=""$IPVM"" -dc=""nano-swarm""" | Out-Null 395 | nssm set SwarmMSKit-ConsulAgent Description "Discovery Service and Key/Value Store use for our Cluster Swarm NanoServer Windows" | Out-Null 396 | nssm set SwarmMSKit-ConsulAgent AppStdout C:\Logs\vault-consul\service-consul.log | Out-Null 397 | nssm set SwarmMSKit-ConsulAgent AppStderr C:\Logs\vault-consul\service-consul.log | Out-Null 398 | nssm start SwarmMSKit-ConsulAgent | Out-Null 399 | 400 | " " 401 | consul join -rpc-addr="$ConsulServerReference" $IPVM 402 | " " 403 | } 404 | 405 | # function to install and configure the Swarm Node/Worker 406 | Function global:SwarmWorker { 407 | 408 | param ([String]$IPVM,[bool]$EnabledDockerDaemonTLS,[String]$IPConsulMaster) 409 | 410 | if ($EnabledDockerDaemonTLS -eq "$True") { 411 | 412 | $advertise = "--advertise=" + $IPVM + ":2376" 413 | 414 | } else { 415 | 416 | $advertise = "--advertise=" + $IPVM + ":2375" 417 | 418 | } 419 | 420 | 421 | $consul = $IPConsulMaster + ":8500" 422 | 423 | # we install the Swarm Node/Worker as an Windows Service with nssm 424 | " " 425 | nssm install SwarmMSKit-Worker swarm "join $advertise consul://$consul" | Out-Null 426 | nssm set SwarmMSKit-Worker Description "Cluster Swarm under NanoServer - Swarm Node" | Out-Null 427 | nssm set SwarmMSKit-Worker AppStdout c:\Logs\swarm\service.log | Out-Null 428 | nssm set SwarmMSKit-Worker AppStderr c:\Logs\swarm\service.log | Out-Null 429 | nssm start SwarmMSKit-Worker | Out-Null 430 | " " 431 | } 432 | 433 | # function to install and configure the Swarm Manager 434 | Function global:SwarmManager { 435 | 436 | param ([String]$IPVM,[bool]$EnabledDockerDaemonTLS,[String]$IPConsulMaster, [int]$SwarmClusterPort) 437 | 438 | $SwarmManagerDaemon = $IPVM + ":$SwarmClusterPort" 439 | $consul = $IPConsulMaster + ":8500" 440 | 441 | if ($EnabledDockerDaemonTLS -eq "$True") { 442 | 443 | $TLSverify = "--tlsverify --tlscacert=C:\\ProgramData\\docker\\certs.d\\ca.pem --tlscert=C:\\ProgramData\\docker\\certs.d\\cert.pem --tlskey=C:\\ProgramData\\docker\\certs.d\\key.pem" 444 | 445 | nssm install SwarmMSKit-Manager swarm manage "$TLSverify -H tcp://$SwarmManagerDaemon consul://$consul" | Out-Null 446 | 447 | } else { 448 | 449 | # we install the Swarm Manager as an Windows Service with nssm 450 | nssm install SwarmMSKit-Manager swarm manage "-H tcp://$SwarmManagerDaemon consul://$consul" | Out-Null 451 | 452 | } 453 | nssm set SwarmMSKit-Manager Description "Cluster Swarm under NanoServer - Swarm Manager" | Out-Null 454 | nssm set SwarmMSKit-Manager AppStdout c:\Logs\swarm\service.log | Out-Null 455 | nssm set SwarmMSKit-Manager AppStderr c:\Logs\swarm\service.log | Out-Null 456 | nssm start SwarmMSKit-Manager | Out-Null 457 | } 458 | 459 | # This function will create a start execution script and will be execute when the NanoServer VM will be boot the first time : set all the FW rules for Docker, Consul, Swarm, Private Registry (Nexus OSS Free binaries repository) for our docker images, WinRM, File Sharing, etc .. and 460 | # performing several tasks like disable ipv6 on all adapters, disable all unused adapters, set the Time zone (Paris in this case) and set the static IPv4 configuration, etc ... 461 | Function global:NanoSetup { 462 | 463 | param([bool]$FireWallRules,[int]$SwarmClusterPort) 464 | 465 | 466 | if ($FireWallRules -eq "$True") { 467 | 468 | " " 469 | "Set all the Firewall Rules for Docker Daemon, Consul, Swarm, Private Registry (Nexus OSS Free binaries repository) for our Docker images, WinRM, File Sharing" 470 | "Performing several tasks like disable ipv6 on all adapters, disable all unused adapters, set the Time zone (Paris in this case) etc ... Please Wait ..." 471 | " " 472 | 473 | # Below, several FW Rules for enabled file sharing access to the container, Docker Daemon, Swarm, Consul, Vault, etc ... 474 | 475 | # Enabled file sharing access to the Container 476 | netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=yes 477 | 478 | # DockerDaemon default TCP Ports : 2375 and 2376 (TLS) 479 | netsh advfirewall firewall add rule name="SwarmMSKit - Docker daemon 2375" dir=in action=allow protocol=TCP localport=2375 480 | netsh advfirewall firewall add rule name="SwarmMSKit - Docker daemon TLS 2376" dir=in action=allow protocol=TCP localport=2376 481 | 482 | netsh advfirewall firewall add rule name="SwarmMSKit - Docker daemon Swarm $SwarmClusterPort" dir=in action=allow protocol=TCP localport=$SwarmClusterPort 483 | 484 | # Consul - Server RPC (Default 8300). This is used by servers to handle incoming requests from other agents. TCP only. 485 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - Server RPC" dir=in action=allow protocol=TCP localport=8300 486 | 487 | # Consul - Serf LAN (Default 8301). This is used to handle gossip in the LAN. Required by all agents. TCP and UDP. 488 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - Serf LAN - TCP" dir=in action=allow protocol=TCP localport=8301 489 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - Serf LAN - UDP" dir=in action=allow protocol=UDP localport=8301 490 | 491 | # Consul - Serf WAN (Default 8302). This is used by servers to gossip over the WAN to other servers. TCP and UDP. 492 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - Serf WAN - TCP" dir=in action=allow protocol=TCP localport=8302 493 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - Serf WAN - UDP" dir=in action=allow protocol=UDP localport=8302 494 | 495 | # Consul - CLI RPC (Default 8400). This is used by all agents to handle RPC from the CLI. TCP only. 496 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - CLI RPC" dir=in action=allow protocol=TCP localport=8400 497 | 498 | # Consul - HTTP API (Default 8500). This is used by clients to talk to the HTTP API. TCP only. 499 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - HTTP API" dir=in action=allow protocol=TCP localport=8500 500 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - HTTP API" dir=out action=allow protocol=TCP localport=8500 501 | 502 | # Consul - DNS Interface (Default 8600). Used to resolve DNS queries. TCP and UDP. 503 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - DNS Interface - TCP" dir=out action=allow protocol=TCP localport=8600 504 | netsh advfirewall firewall add rule name="SwarmMSKit - Consul - DNS Interface - UDP" dir=out action=allow protocol=UDP localport=8600 505 | 506 | # Vault (Default 8200). 507 | netsh advfirewall firewall add rule name="SwarmMSKit - Vault - Server" dir=in action=allow protocol=TCP localport=8200 508 | 509 | # Nexus for our Private Registry (Default 8081 and 8123). 510 | netsh advfirewall firewall add rule name="SwarmMSKit - Nexus IHM - Private Registry" dir=in action=allow protocol=TCP localport=8081 511 | netsh advfirewall firewall add rule name="SwarmMSKit - Private Registry" dir=in action=allow protocol=TCP localport=8123 512 | 513 | # Portainer - UCP - Interface web for the Cluster Swarm Administration 514 | netsh advfirewall firewall add rule name="SwarmMSKit - Portainer - UCP" dir=in action=allow protocol=TCP localport=9000 515 | 516 | } 517 | else { 518 | 519 | " " 520 | "Disable Microsoft Firewall ..." 521 | " " 522 | netsh Advfirewall set allprofiles state off 523 | 524 | } 525 | 526 | #disable ipv6 on all adapters 527 | Get-NetAdapterBinding -ComponentID 'ms_tcpip6' | disable-NetAdapterBinding -ComponentID ms_tcpip6 -PassThru 528 | 529 | #disable all unused adapters 530 | Get-NetAdapter | ? { $_.status -eq "Disconnected" } | Disable-NetAdapter -Confirm:$false 531 | 532 | $Name = "Paris" 533 | $SetTimeZoneParis = [system.timezoneinfo]::GetSystemTimeZones() | Where-Object {$_.ID -like "*$Name*" -or $_.DisplayName -like "*$Name*" } | Select-Object -ExpandProperty ID 534 | tzutil.exe /s $SetTimeZoneParis 535 | 536 | #required for some cmdlets to work properly 537 | set LOCALAPPDATA=%USERPROFILE%\AppData\Local 538 | 539 | } 540 | 541 | # function for setting the TCP/IP for the new VM NanoServer (static @IP) 542 | Function Set-VMNetworkConfiguration { 543 | [CmdletBinding()] 544 | Param ( 545 | 546 | [Parameter(Mandatory=$true, 547 | Position=1, 548 | ParameterSetName='DHCP', 549 | ValueFromPipeline=$true)] 550 | [Parameter(Mandatory=$true, 551 | Position=0, 552 | ParameterSetName='Static', 553 | ValueFromPipeline=$true)] 554 | [Microsoft.HyperV.PowerShell.VMNetworkAdapter]$NetworkAdapter, 555 | 556 | [Parameter(Mandatory=$true, 557 | Position=1, 558 | ParameterSetName='Static')] 559 | [String[]]$IPAddress=@(), 560 | 561 | [Parameter(Mandatory=$false, 562 | Position=2, 563 | ParameterSetName='Static')] 564 | [String[]]$Subnet=@(), 565 | 566 | [Parameter(Mandatory=$false, 567 | Position=3, 568 | ParameterSetName='Static')] 569 | [String[]]$DefaultGateway = @(), 570 | 571 | [Parameter(Mandatory=$false, 572 | Position=4, 573 | ParameterSetName='Static')] 574 | [String[]]$DNSServer = @(), 575 | 576 | [Parameter(Mandatory=$false, 577 | Position=0, 578 | ParameterSetName='DHCP')] 579 | [Switch]$Dhcp 580 | ) 581 | 582 | $VM = Get-WmiObject -Namespace 'root\virtualization\v2' -Class 'Msvm_ComputerSystem' | Where-Object { $_.ElementName -eq $NetworkAdapter.VMName } 583 | $VMSettings = $vm.GetRelated('Msvm_VirtualSystemSettingData') | Where-Object { $_.VirtualSystemType -eq 'Microsoft:Hyper-V:System:Realized' } 584 | $VMNetAdapters = $VMSettings.GetRelated('Msvm_SyntheticEthernetPortSettingData') 585 | 586 | $NetworkSettings = @() 587 | foreach ($NetAdapter in $VMNetAdapters) { 588 | if ($NetAdapter.Address -eq $NetworkAdapter.MacAddress) { 589 | $NetworkSettings = $NetworkSettings + $NetAdapter.GetRelated("Msvm_GuestNetworkAdapterConfiguration") 590 | } 591 | } 592 | 593 | $NetworkSettings[0].IPAddresses = $IPAddress 594 | $NetworkSettings[0].Subnets = $Subnet 595 | $NetworkSettings[0].DefaultGateways = $DefaultGateway 596 | $NetworkSettings[0].DNSServers = $DNSServer 597 | $NetworkSettings[0].ProtocolIFType = 4096 598 | 599 | if ($dhcp) { 600 | $NetworkSettings[0].DHCPEnabled = $true 601 | } else { 602 | $NetworkSettings[0].DHCPEnabled = $false 603 | } 604 | 605 | $Service = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\virtualization\v2" 606 | $setIP = $Service.SetGuestNetworkAdapterConfiguration($VM, $NetworkSettings[0].GetText(1)) 607 | 608 | if ($setip.ReturnValue -eq 4096) { 609 | $job=[WMI]$setip.job 610 | 611 | while ($job.JobState -eq 3 -or $job.JobState -eq 4) { 612 | start-sleep 1 613 | $job=[WMI]$setip.job 614 | } 615 | 616 | if ($job.JobState -eq 7) { 617 | "Success update the TCP/IP configuration !" 618 | } 619 | else { 620 | $job.GetError() 621 | } 622 | } elseif($setip.ReturnValue -eq 0) { 623 | "Success update the TCP/IP configuration !" 624 | } 625 | } 626 | 627 | # function for setting the TCP/IP for the new VM NanoServer (static @IP) 628 | Function DockerSwarmMSKitWithTLSAuthentication { 629 | 630 | param([String]$ServersInCluster,[String]$IPAddress,[String]$FirstVM,[String]$Subnet) 631 | 632 | if ($IPAddress -eq $FirstVM) { 633 | 634 | " " 635 | "All the Docker Engine hosts (client, swarm manager(s) and swarm workers) have a copy of the CA’s certificate as well as their own key-pair signed by the CA." 636 | "TLS Authentication will be automatically set and configure between Docker, Swarm and Client :" 637 | " " 638 | "Create a Certificate Authority (CA) server, Create and sign keys for the Swarm Manager and Workers ... Please Wait ..." 639 | " " 640 | 641 | $OpenSSLPath = "$global:VMPath\nanoserver-offine-temp\ProgramData\OpenSSL\bin" 642 | 643 | New-Item -Type Directory -Path "$global:VMPath\nanoserver-offine-temp\ProgramData\OpenSSL\TLS" -Force | Out-Null 644 | 645 | $KeyStoreTLS = "$global:VMPath\nanoserver-offine-temp\ProgramData\OpenSSL\TLS" 646 | 647 | 648 | $LastOctetAdress = $IPAddress.Split('.') 649 | $NextIP = [int]($LastOctetAdress[-1]) 650 | 651 | $j=2 652 | $a = 653 | for($i = $NextIP; $i -lt ($NextIP+$ServersInCluster); $i++){ 654 | "IP."+$j+" = $Subnet$i`r" 655 | $j++ 656 | } 657 | 658 | $opensslcnf = @" 659 | [ req ] 660 | default_bits = 4096 661 | default_keyfile = ca-priv-key.pem 662 | distinguished_name = req_distinguished_name 663 | x509_extensions = v3_ca 664 | default_md = sha1 665 | string_mask = nombstr 666 | req_extensions = v3_req 667 | prompt = no 668 | 669 | [req_distinguished_name] 670 | countryName = FR 671 | stateOrProvinceName = IdF 672 | localityName = PARIS 673 | organizationalUnitName = SwarmMSKit 674 | 675 | [ v3_req ] 676 | # Extensions to add to a certificate request 677 | basicConstraints = CA:FALSE 678 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 679 | subjectAltName = @alt_names 680 | 681 | [ v3_ca ] 682 | subjectAltName = @alt_names 683 | subjectKeyIdentifier=hash 684 | authorityKeyIdentifier=keyid:always,issuer 685 | basicConstraints = CA:true 686 | 687 | [ crl_ext ] 688 | authorityKeyIdentifier=keyid:always 689 | 690 | [ alt_names ] 691 | # The IPs of the Docker and Swarm hosts 692 | IP.1 = 127.0.0.1 693 | $a 694 | "@ 695 | 696 | $opensslcnf | Out-File $KeyStoreTLS\openssl.cnf 697 | 698 | (Get-Content -path "$KeyStoreTLS\openssl.cnf" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$KeyStoreTLS\openssl.cnf" 699 | 700 | #New-Item -Type Directory -Path "$global:VMPath\nanoserver-offine-temp\certs.d" -Force | Out-Null 701 | New-Item -Type Directory -Path "$global:VMPath\nanoserver-offine-temp\ProgramData\docker\certs.d" -Force | Out-Host | Out-Null 702 | 703 | " " 704 | "$OpenSSLPath\openssl.exe -ArgumentList genrsa -out $KeyStoreTLS\ca-priv-key.pem 2048" 705 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "genrsa -out $KeyStoreTLS\ca-priv-key.pem 2048" | Out-Host | Out-Null 706 | Start-Sleep -Seconds 5 707 | " " 708 | gc $KeyStoreTLS\ca-priv-key.pem 709 | 710 | 711 | " " 712 | "$OpenSSLPath\openssl.exe -ArgumentList req -config $KeyStoreTLS\openssl.cnf -new -key $KeyStoreTLS\ca-priv-key.pem -x509 -days 1825 -out $KeyStoreTLS\ca.pem" 713 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "req -config $KeyStoreTLS\openssl.cnf -new -key $KeyStoreTLS\ca-priv-key.pem -x509 -days 1825 -out $KeyStoreTLS\ca.pem" | Out-Null 714 | Start-Sleep -Seconds 5 715 | " " 716 | gc $KeyStoreTLS\ca.pem 717 | 718 | 719 | if (!(Test-Path -Path "$env:USERPROFILE\.SwarmMSKit")) {" ";"Create $env:USERPROFILE\.SwarmMSKit folder and copy the certificates for connecting to a TLS-enabled daemon with TLS ... Please Wait ...";" "; 720 | mkdir "$env:USERPROFILE\.SwarmMSKit" | Out-Host | Out-Null 721 | } else { 722 | 723 | Remove-Item -Recurse $env:USERPROFILE\.SwarmMSKit\* -Force 724 | 725 | } 726 | 727 | " " 728 | "$OpenSSLPath\openssl.exe -ArgumentList genrsa -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem 2048" 729 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "genrsa -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem 2048" | Out-Host | Out-Null 730 | Start-Sleep -Seconds 5 731 | " " 732 | gc $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem 733 | 734 | " " 735 | "$OpenSSLPath\openssl.exe -ArgumentList req -subj /CN=127.0.0.1 -new -key $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr" 736 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "req -subj ""/CN=127.0.0.1"" -new -key $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr" | Out-Null 737 | Start-Sleep -Seconds 5 738 | " " 739 | gc $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr 740 | 741 | " " 742 | "$OpenSSLPath\openssl.exe -ArgumentList x509 -req -days 1825 -in $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr -CA $KeyStoreTLS\ca.pem -CAkey $KeyStoreTLS\ca-priv-key.pem -CAcreateserial -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1-cert.pem -extensions v3_req -extfile $KeyStoreTLS\openssl.cnf" 743 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "x509 -req -days 1825 -in $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr -CA $KeyStoreTLS\ca.pem -CAkey $KeyStoreTLS\ca-priv-key.pem -CAcreateserial -out $env:USERPROFILE\.SwarmMSKit\127.0.0.1-cert.pem -extensions v3_req -extfile $KeyStoreTLS\openssl.cnf" | Out-Host | Out-Null 744 | Start-Sleep -Seconds 5 745 | " " 746 | gc $env:USERPROFILE\.SwarmMSKit\127.0.0.1-cert.pem 747 | " " 748 | 749 | Copy-Item -Path $KeyStoreTLS\ca.pem -Destination $env:USERPROFILE\.SwarmMSKit\ca.pem | Out-Host | Out-Null 750 | Rename-Item -Path $env:USERPROFILE\.SwarmMSKit\127.0.0.1-cert.pem -NewName $env:USERPROFILE\.SwarmMSKit\cert.pem | Out-Host | Out-Null 751 | Rename-Item -Path $env:USERPROFILE\.SwarmMSKit\127.0.0.1-priv-key.pem -NewName $env:USERPROFILE\.SwarmMSKit\key.pem | Out-Host | Out-Null 752 | 753 | " " 754 | "You have now a copy of the CA’s certificate as well as their own key-pair signed by the CA for executing Docker command on the Cluster Swarm with a TLS Authentication." 755 | "All the certificates are save in this folder : $env:USERPROFILE\.SwarmMSKit" 756 | " " 757 | 758 | $LastOctetAdress = $IPAddress.Split('.') 759 | $NextIP = [int]($LastOctetAdress[-1]) 760 | 761 | for($i = $NextIP; $i -lt ($NextIP+$ServersInCluster); $i++){ 762 | 763 | " " 764 | "$OpenSSLPath\openssl.exe -ArgumentList genrsa -out $KeyStoreTLS\$Subnet$i-priv-key.pem 2048" 765 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "genrsa -out $KeyStoreTLS\$Subnet$i-priv-key.pem 2048" | Out-Host | Out-Null 766 | Start-Sleep -Seconds 5 767 | " " 768 | gc $KeyStoreTLS\$Subnet$i-priv-key.pem 769 | 770 | " " 771 | "$OpenSSLPath\openssl.exe -ArgumentList req -subj /CN=$Subnet$i -new -key $KeyStoreTLS\$Subnet$i-priv-key.pem -out $KeyStoreTLS\$Subnet$i.csr" 772 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "req -subj ""/CN=$Subnet$i"" -new -key $KeyStoreTLS\$Subnet$i-priv-key.pem -out $KeyStoreTLS\$Subnet$i.csr" | Out-Host | Out-Null 773 | Start-Sleep -Seconds 5 774 | " " 775 | gc $KeyStoreTLS\$Subnet$i.csr 776 | 777 | " " 778 | "$OpenSSLPath\openssl.exe -ArgumentList x509 -req -days 1825 -in $KeyStoreTLS\$Subnet$i.csr -CA $KeyStoreTLS\ca.pem -CAkey $KeyStoreTLS\ca-priv-key.pem -CAcreateserial -out $KeyStoreTLS\$Subnet$i-cert.pem -extensions v3_req -extfile $KeyStoreTLS\openssl.cnf" 779 | Start-Process -FilePath "$OpenSSLPath\openssl.exe" -ArgumentList "x509 -req -days 1825 -in $KeyStoreTLS\$Subnet$i.csr -CA $KeyStoreTLS\ca.pem -CAkey $KeyStoreTLS\ca-priv-key.pem -CAcreateserial -out $KeyStoreTLS\$Subnet$i-cert.pem -extensions v3_req -extfile $KeyStoreTLS\openssl.cnf" | Out-Host | Out-Null 780 | Start-Sleep -Seconds 5 781 | " " 782 | gc $KeyStoreTLS\$Subnet$i-cert.pem 783 | " " 784 | 785 | if ((Test-Path -Path $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr)) { 786 | Remove-Item -Path $env:USERPROFILE\.SwarmMSKit\127.0.0.1.csr -Force | Out-Null 787 | } 788 | 789 | "" 790 | "The Docker Engine host which have this [@IP:$Subnet$i] will have a copy of the CA’s certificate as well as their own key-pair signed by the CA." 791 | " " 792 | 793 | } 794 | } else { 795 | 796 | " " 797 | "The Certifactes Generated for this [$IPAddress] NanoServer have been already generated/performed !" 798 | "Copying them and configuring them for the Daemon Docker Engine ... Please Wait ..." 799 | " " 800 | } 801 | 802 | 803 | } 804 | 805 | Function SwarMSKit-Check { 806 | 807 | param([String]$IPAddress,[String]$SwarmClusterPort,[bool]$EnabledDockerDaemonTLS,[String]$Username,[String]$clearadminPassword) 808 | 809 | $TLSCertificatesPath = "$env:USERPROFILE\.SwarmMSKit" 810 | 811 | if ($EnabledDockerDaemonTLS -eq "$True") { 812 | $TLSverify = "--tlsverify --tlscacert=$TLSCertificatesPath\ca.pem --tlscert=$TLSCertificatesPath\cert.pem --tlskey=$TLSCertificatesPath\key.pem" 813 | } else { 814 | $TLSverify ="" 815 | } 816 | 817 | $Source = "\\$IPAddress\c$\Windows\System32" 818 | $Dest = "$TLSCertificatesPath" 819 | 820 | if (!(Test-Path S:)) { 821 | 822 | $MapDrive ="net use S: $Source /user:$Username $clearadminPassword" 823 | Invoke-Expression $MapDrive | Out-Host | Out-Null 824 | } 825 | else { 826 | 827 | 828 | $MapDriveDelete ="net use S: /delete" 829 | Invoke-Expression $MapDriveDelete | Out-Host | Out-Null 830 | $MapDrive ="net use S: $Source /user:$Username $clearadminPassword" 831 | Invoke-Expression $MapDrive | Out-Host | Out-Null 832 | 833 | } 834 | 835 | Copy-Item -Path $Source\vault.exe -Destination $Dest\vault.exe 836 | Copy-Item -Path $Source\consul.exe -Destination $Dest\consul.exe 837 | 838 | $MapDriveDelete ="net use S: /delete" 839 | Invoke-Expression $MapDriveDelete | Out-Host | Out-Null 840 | 841 | 842 | $batchTestPostSwarMSKitInstallation = "@ 843 | @echo off 844 | echo. 845 | echo @@@@@ SwarmMSKit has been deployed ! Let's verify it :) ! @@@@@ 846 | echo. 847 | pause 848 | echo. 849 | echo. 850 | echo ***** CONSUL SERVER STATUS : 851 | echo. 852 | $TLSCertificatesPath\consul members -rpc-addr=$IPAddress`:8400 853 | echo. 854 | pause 855 | echo. 856 | echo ***** VAULT SERVER STATUS : 857 | echo. 858 | $TLSCertificatesPath\vault status 859 | echo. 860 | pause 861 | echo. 862 | echo ***** SWARM CLUSTER STATUS : 863 | echo. 864 | docker -H tcp://$IPAddress`:$SwarmClusterPort $TLSverify info 865 | echo. 866 | pause 867 | echo. 868 | echo ***** RUN A CONTAINER IN THE CLUSTER SWARM (nanoserver cmd) : 869 | echo. 870 | echo command will be launch is : docker -H tcp://$IPAddress`:$SwarmClusterPort $TLSverify run -it nanoserver cmd 871 | echo. 872 | pause 873 | docker -H tcp://$IPAddress`:$SwarmClusterPort $TLSverify run -it nanoserver cmd 874 | echo. 875 | echo. 876 | echo. 877 | echo. Thank you for using SwarmMSKit and stay tuned for the next update/add ... 878 | echo. 879 | echo. 880 | pause 881 | echo. 882 | @" 883 | 884 | $batchTestPostSwarMSKitInstallation | Out-File $env:USERPROFILE\Desktop\SwarmMSKIT-Check.cmd 885 | 886 | (Get-Content -path "$env:USERPROFILE\Desktop\SwarmMSKIT-Check.cmd" -Encoding Unicode) | Set-Content -Encoding "Default" -Path "$env:USERPROFILE\Desktop\SwarmMSKIT-Check.cmd" 887 | 888 | Start-Process -FilePath http://$IPAddress`:9000 889 | Start-Process -FilePath http://$IPAddress`:8081 890 | 891 | Start-Process -FilePath "$env:USERPROFILE\Desktop\SwarmMSKIT-Check.cmd" 892 | 893 | } 894 | --------------------------------------------------------------------------------