From b27c3b8e3a0c1d9eac694292aa1fdc81288bb53c Mon Sep 17 00:00:00 2001 From: Ben Gelens Date: Mon, 14 Dec 2015 16:53:42 +0100 Subject: [PATCH] TP4 and Nano compatible, HyperV Container support, Container Hostname support --- Configuration.ps1 | 14 +++-- README.md | 34 +++++++---- cWindowsContainer.psd1 | 5 +- cWindowsContainer.psm1 | 124 ++++++++++++++++++++++++++++------------- 4 files changed, 120 insertions(+), 57 deletions(-) diff --git a/Configuration.ps1 b/Configuration.ps1 index efb1297..921416d 100644 --- a/Configuration.ps1 +++ b/Configuration.ps1 @@ -1,10 +1,11 @@ configuration NewContainer { - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyAppContainer { Ensure = 'Present' Name = 'MyAppContainer' StartUpScript = '"Hello World" | out-file c:\hello.txt' + ContainerImageName = 'NanoServer' } } NewContainer @@ -14,12 +15,15 @@ configuration MultiLineConfigContainer { param ( [String] $StartupScript ) - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyDCContainer { Ensure = 'Present' Name = 'MyDCContainer' StartUpScript = $StartupScript + ContainerImageName = 'NanoServer' + ContainerType = 'HyperV' + ContainerComputerName = 'MyContainer' } } @@ -33,11 +37,12 @@ Start-DscConfiguration .\MultiLineConfigContainer -Wait -Verbose configuration RemContainer { - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyAppContainer { Ensure = 'Absent' Name = 'MyAppContainer' + ContainerImageName = 'NanoServer' } } RemContainer @@ -47,13 +52,14 @@ configuration ContainerNginX { param ( [String] $StartupScript ) - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer NginX { Ensure = 'Present' Name = 'NginX' StartUpScript = $StartupScript SwitchName = 'Virtual Switch' + ContainerImageName = 'WindowsServerCore' } } diff --git a/README.md b/README.md index 016f471..87e5314 100644 --- a/README.md +++ b/README.md @@ -3,15 +3,24 @@ cWindowsContainer Class Based DSC resource to deploy Windows Containers. -**Deploy container from default image with single line startup script** +**Update Sep 2015** +Get-DscConfiguration now shows ContainerId and currently assigned IP Address +![Config](https://github.com/bgelens/cWindowsContainer/blob/master/GetDSCConfigIPandID.jpg) +**Update Dec 2015** +Resource updated for TP4 (TP3 must use version 1.0). +Now supports HyperV container type and the defining of the hostname within the container. +Validated Nano Server compatibility + +**Deploy container from Nano image with single line startup script** ```powershell configuration NewContainer { - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyAppContainer { Ensure = 'Present' Name = 'MyAppContainer' StartUpScript = '"Hello World" | out-file c:\hello.txt' + ContainerImageName = 'NanoServer' } } NewContainer @@ -19,18 +28,21 @@ Start-DscConfiguration .\NewContainer -Wait -Verbose ``` ![Config](https://github.com/bgelens/cWindowsContainer/blob/master/newcontainerconfig.jpg) -**Deploy container from default image with multi-line startup script** +**Deploy container from Nano image with multi-line startup script and specifying Container Hostname** ````powershell configuration MultiLineConfigContainer { param ( [String] $StartupScript ) - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyDCContainer { Ensure = 'Present' Name = 'MyDCContainer' StartUpScript = $StartupScript + ContainerImageName = 'NanoServer' + ContainerType = 'HyperV' + ContainerComputerName = 'MyContainer' } } @@ -46,29 +58,31 @@ Start-DscConfiguration .\MultiLineConfigContainer -Wait -Verbose **Remove container** ```powershell configuration RemContainer { - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer MyAppContainer { Ensure = 'Absent' Name = 'MyAppContainer' + ContainerImageName = 'NanoServer' } } RemContainer Start-DscConfiguration .\RemContainer -Wait -Verbose ``` -**NGINX install and Network** +**NGINX install and Network (does not work on Nano as Invoke-WebRequest is not available)** ```powershell configuration ContainerNginX { param ( [String] $StartupScript ) - Import-DscResource -ModuleName cWindowsContainer + Import-DscResource -ModuleName cWindowsContainer -ModuleVersion 1.1 cWindowsContainer NginX { Ensure = 'Present' Name = 'NginX' StartUpScript = $StartupScript SwitchName = 'Virtual Switch' + ContainerImageName = 'WindowsServerCore' } } @@ -82,8 +96,4 @@ Start-Process nginx ContainerNginX -StartupScript $script Start-DscConfiguration .\ContainerNginX -Wait -Verbose -Force -``` - -**Update** -Get-DscConfiguration now shows ContainerId and currently assigned IP Address -![Config](https://github.com/bgelens/cWindowsContainer/blob/master/GetDSCConfigIPandID.jpg) \ No newline at end of file +``` \ No newline at end of file diff --git a/cWindowsContainer.psd1 b/cWindowsContainer.psd1 index 2e1b6a7..601f2bb 100644 --- a/cWindowsContainer.psd1 +++ b/cWindowsContainer.psd1 @@ -1,5 +1,5 @@ # -# Module manifest for module 'NewManifest' +# Module manifest for module 'cWindowsContainer' # # Generated by: Ben Gelens # @@ -12,7 +12,7 @@ RootModule = 'cWindowsContainer.psm1' # Version number of this module. -ModuleVersion = '1.0' +ModuleVersion = '1.1' # ID used to uniquely identify this module GUID = 'bd4390dc-a8ad-4bce-8d69-f53ccf8e4163' @@ -120,4 +120,3 @@ HelpInfoURI = 'https://github.com/bgelens/cWindowsContainer' # DefaultCommandPrefix = '' } - diff --git a/cWindowsContainer.psm1 b/cWindowsContainer.psm1 index b3f6bbd..b5e2f61 100644 --- a/cWindowsContainer.psm1 +++ b/cWindowsContainer.psm1 @@ -1,6 +1,11 @@ enum Ensure { - Absent; - Present; + Absent + Present +} + +enum ContainerType { + Default + HyperV } [DscResource()] @@ -11,8 +16,14 @@ class cWindowsContainer { [DscProperty(Mandatory)] [Ensure] $Ensure + [DscProperty(Mandatory)] + [String] $ContainerImageName + [DscProperty()] - [String] $ContainerImage = 'WindowsServerCore' + [String] $ContainerImagePublisher + + [DscProperty()] + [String] $ContainerImageVersion [DscProperty()] [String] $SwitchName @@ -26,71 +37,108 @@ class cWindowsContainer { [DscProperty(NotConfigurable)] [String] $ContainerId + [DscProperty()] + [String] $ContainerComputerName + + [DscProperty()] + [ContainerType] $ContainerType = [ContainerType]::Default + [void] Set () { - $ErrorActionPreference = 'Stop' try { if ($this.Ensure -eq [Ensure]::Present) { - Write-Verbose -Message 'Creating Container'; - if ($null -eq ($Image = Get-ContainerImage -Name $this.ContainerImage)) { - Write-Error -Message "ContainerImage with name $($this.ContainerImage) was not found"; + Write-Verbose -Message 'Starting creation of new Container' + + #region start build New-Container parameters + $ContainerNewParams = [System.Collections.Hashtable]::new() + $ContainerNewParams.Add('Name',$this.Name) + $ContainerNewParams.Add('RuntimeType',$this.ContainerType) + if ($null -ne $this.ContainerComputerName) { + $ContainerNewParams.Add('ContainerComputerName',$this.ContainerComputerName) } + #endregion start build New-Container parameters - $Params = @{ - Name = $this.Name; - ContainerImage = $Image; + #region ContainerImage + $ContainerImageParams = [System.Collections.Hashtable]::new() + $ContainerImageParams.Add('Name',$this.ContainerImageName) + if ($null -ne $this.ContainerImagePublisher) { + $ContainerImageParams.Add('Publisher',$this.ContainerImagePublisher) + } + if ($null -ne $this.ContainerImageVersion) { + $ContainerImageParams.Add('Version',$this.ContainerImageVersion) + } + Write-Verbose -Message "Searching for image: $($ContainerImageParams | Out-String)" + if ($null -eq ($Image = Get-ContainerImage @ContainerImageParams)) { + Write-Error -Message "ContainerImage with properties $($ContainerImageParams | Out-String) was not found" -ErrorAction Stop + } else { + $ContainerNewParams.Add('ContainerImage',$Image) } + #endregion ContainerImage + + #region Switch + Write-Verbose -Message "Searching for specified switch: $($this.SwitchName)" if ($this.SwitchName -and ($null -ne (Get-VMSwitch -Name $this.SwitchName))) { - Write-Verbose -Message 'VMSwitch was found and will be bound'; - $Params += @{ - SwitchName = $this.SwitchName; - } + Write-Verbose -Message 'Switch was found and will be bound' + $ContainerNewParams.Add('SwitchName',$this.SwitchName) } elseif ($this.SwitchName -and ($null -eq (Get-VMSwitch -Name $this.SwitchName))) { - Write-Error -Message "VMSwitch with name $($this.SwitchName) was not found"; + Write-Error -Message "Switch with name $($this.SwitchName) was not found" -ErrorAction Stop } - $Container = New-Container @Params; - $Container | Start-Container | Out-Null; - + #endregion Switch + + #region Create and start Container + Write-Verbose -Message "Creating Container: $($ContainerNewParams | Out-String)" + $Container = New-Container @ContainerNewParams + $Container | Start-Container + #endregion Create and start Container + + #region run startup script if ($null -ne $this.StartUpScript) { Write-Verbose -Message 'Startup Script specified, passing script to InvokeScript method' - [void] $this.InvokeScript(([scriptblock]::Create($this.StartUpScript)),$Container.ContainerId); + [void] $this.InvokeScript(([scriptblock]::Create($this.StartUpScript)),$Container.ContainerId) } + #endregion run startup script } else { - Write-Verbose -Message 'Removing Container'; - Get-Container -Name $this.Name | Stop-Container -Passthru | Remove-Container -Force | Out-Null; + Write-Verbose -Message 'Removing Container' + Get-Container -Name $this.Name | Stop-Container -Passthru | Remove-Container -Force } } catch { - Write-Error -ErrorRecord $_ -ErrorAction Stop; + Write-Error -ErrorRecord $_ -ErrorAction Stop } } [bool] Test () { if ((Get-Container -Name $this.Name -ErrorAction SilentlyContinue) -and ($this.Ensure -eq [Ensure]::Present)) { - return $true; + return $true } else { - return $false; + return $false } } [String] InvokeScript ([String] $Script, [String] $ContainerId) { - $Output = Invoke-Command -ContainerId $ContainerId -RunAsAdministrator -ScriptBlock ([scriptblock]::Create($Script)) -ErrorAction SilentlyContinue; - return $Output; + $Output = Invoke-Command -ContainerId $ContainerId -RunAsAdministrator -ScriptBlock ([scriptblock]::Create($Script)) -ErrorAction Stop + return $Output } [cWindowsContainer] Get () { - $Configuration = [hashtable]::new(); - $Configuration.Add('Name',$this.Name); - $Configuration.Add('ContainerImage',$this.ContainerImage); - $Configuration.Add('SwitchName',$this.SwitchName); - $Configuration.Add('StartUpScript',$this.StartUpScript); + $Configuration = [System.Collections.Hashtable]::new() + $Configuration.Add('Name',$this.Name) + $Configuration.Add('ContainerComputerName',$this.ContainerComputerName) + $Configuration.Add('ContainerImageName',$this.ContainerImageName) + $Configuration.Add('ContainerImagePublisher',$this.ContainerImagePublisher) + $Configuration.Add('ContainerImageVersion',$this.ContainerImageVersion) + $Configuration.Add('SwitchName',$this.SwitchName) + $Configuration.Add('StartUpScript',$this.StartUpScript) + $Configuration.Add('ContainerType',$this.ContainerType) if (($this.Ensure -eq [Ensure]::Present) -and ($this.Test())) { - Write-Verbose -Message 'Acquiring ContainerId'; - $Configuration.Add('ContainerId',(Get-Container -Name $this.Name).ContainerId); - $Configuration.Add('Ensure','Present'); - Write-Verbose -Message 'Acquiring IPAddress'; - $Configuration.Add('IPAddress',$this.InvokeScript('(Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual).IPAddress',$Configuration.ContainerId)); + Write-Verbose -Message 'Acquiring ContainerId' + $Configuration.Add('ContainerId',(Get-Container -Name $this.Name).ContainerId) + $Configuration.Add('Ensure','Present') + Write-Verbose -Message 'Acquiring IPAddress' + if ($null -ne $this.SwitchName) { + $Configuration.Add('IPAddress',$this.InvokeScript('(Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Manual).IPAddress',$Configuration.ContainerId)) + } } else { - $Configuration.Add('Ensure','Absent'); + $Configuration.Add('Ensure','Absent') } - return $Configuration; + return $Configuration } } \ No newline at end of file