From 1f051d2de7e702970ed7cd476b1004147b270c93 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 15 Jul 2015 12:37:27 -0500 Subject: [PATCH 01/16] Update cTentacleAgent.psm1 Found that the tentacle agent doesn't need a port to register with the Octopus Server. Removed the TCPTest check and port parameter from the Get-IP function --- DSCResources/cTentacleAgent/cTentacleAgent.psm1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 6004947d0..0a8cdfd56 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -228,7 +228,7 @@ function Invoke-AndAssert { # After the Tentacle is registered with Octopus, Tentacle listens on a TCP port, and Octopus connects to it. The Octopus server # needs to know the public IP address to use to connect to this Tentacle instance. Is there a way in Windows Azure in which we can # know the public IP/host name of the current machine? -function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl,[int]$port){ +function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ #First Verify the adapter exists $netAdapter = Get-NetAdapter -InterfaceAlias $RegisteredNic -ErrorAction SilentlyContinue if($netAdapter -eq $null){ @@ -247,12 +247,12 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O } #Test the connection to the Octopus Server. If it is not reachable, throw an error - $urlRegex = ‘([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?’ + $urlRegex = ‘([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?’ if($OctopusServerUrl -match $urlRegex){ $OctopusServerUrl = $OctopusServerUrl.Split("//") | select -Last 1 } - $adapterTest = Test-NetConnection $OctopusServerUrl -Port $port - if(!($adapterTest.TcpTestSucceeded -and ($adapterTest.InterfaceAlias -eq $RegisteredNic))){ + $adapterTest = Test-NetConnection $OctopusServerUrl + if(!($adapterTest.PingSucceeded -and ($adapterTest.InterfaceAlias -eq $RegisteredNic))){ throw "Cannot reach Octopus Server $($OctopusServerUrl) from Network $($RegisteredNic). Please check your connection and try running your configuration again" } else{return $ip} @@ -311,7 +311,7 @@ function New-Tentacle Write-Verbose "Open port $port on Windows Firewall" Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } - $ipAddress = Get-MyPublicIPAddress -RegisteredNic $RegisteredNic -isNatted $isNatted -OctopusServerUrl $octopusServerUrl -port $port + $ipAddress = Get-MyPublicIPAddress -RegisteredNic $RegisteredNic -isNatted $isNatted -OctopusServerUrl $octopusServerUrl Write-Verbose "Public IP address: $ipAddress" Write-Verbose "Configuring and registering Tentacle" @@ -379,4 +379,4 @@ function Remove-TentacleRegistration { Write-Verbose "Could not find Tentacle.exe" } -} \ No newline at end of file +} From 882698b42caedc2acde798c76a29a6150122359e Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 15 Jul 2015 13:37:10 -0500 Subject: [PATCH 02/16] Update cTentacleAgent.psm1 Updated testing so if Server URL contains port number, it can be extracted and tested against. Otherwise the test just looks for a ping response. --- DSCResources/cTentacleAgent/cTentacleAgent.psm1 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 0a8cdfd56..50da40e68 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -251,8 +251,17 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O if($OctopusServerUrl -match $urlRegex){ $OctopusServerUrl = $OctopusServerUrl.Split("//") | select -Last 1 } - $adapterTest = Test-NetConnection $OctopusServerUrl - if(!($adapterTest.PingSucceeded -and ($adapterTest.InterfaceAlias -eq $RegisteredNic))){ + if($OctopusServerUrl.Contains(":")){ + $port = $OctopusServerUrl.Split(":")[1] + $OctopusServerUrl = $OctopusServerUrl.Split(":")[0] + $adapterTest = Test-NetConnection $OctopusServerUrl -port $port + $testResult = $adapterTest.TcpTestSucceeded + } + else{ + $adapterTest = Test-NetConnection $OctopusServerUrl + $testResult = $adapterTest.PingSucceeded + } + if(!($testResult -and ($adapterTest.InterfaceAlias -eq $RegisteredNic))){ throw "Cannot reach Octopus Server $($OctopusServerUrl) from Network $($RegisteredNic). Please check your connection and try running your configuration again" } else{return $ip} From 3078155eda96b846d4147c5975178ed101bdeea1 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 15 Jul 2015 16:24:54 -0500 Subject: [PATCH 03/16] Removed tabs from psd1 --- OctopusDSC.psd1 | Bin 670 -> 656 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/OctopusDSC.psd1 b/OctopusDSC.psd1 index 5689ebbc16c9688fcffcdef333c0b85a97074edb..be6d55bce744f654ac3c08c48dee5bf89b2343a5 100644 GIT binary patch delta 38 wcmV+>0NMYZ1&{>^|NcM#djJgp3Xuyhkvv|JoHCKnRFftFjFYqhHk0fDvi|Z85dZ)H delta 52 zcmbQhI**n6|33$YY6e~gE(XqtocfHM6V2m+#8hJ-aXgffbFwsJFOXc$XbvRrGHwO{ DZA}g! From d58c0b0c3b8db7345b56e5db3eefb39772d18ea4 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 15 Jul 2015 17:49:48 -0500 Subject: [PATCH 04/16] Delete .gitignore --- .gitignore | 176 ----------------------------------------------------- 1 file changed, 176 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9f30c7aff..000000000 --- a/.gitignore +++ /dev/null @@ -1,176 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -x64/ -build/ -bld/ -[Bb]in/ -[Oo]bj/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -#NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding addin-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -_NCrunch_* -.*crunch*.local.xml - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml - -# NuGet Packages Directory -packages/ -## TODO: If the tool you use requires repositories.config uncomment the next line -#!packages/repositories.config - -# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets -# This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) -!packages/build/ - -# Windows Azure Build Output -csx/ -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -sql/ -*.Cache -ClientBin/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -node_modules/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ -SampleConfig.ps1 -SampleConfig/* From 9d0348ec1d0fc040ab71ecd51c859f68c4789706 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 15 Jul 2015 18:13:22 -0500 Subject: [PATCH 05/16] Fixed corruption issue with regex variable --- DSCResources/cTentacleAgent/cTentacleAgent.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 50da40e68..ebf8eb33a 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -247,7 +247,7 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O } #Test the connection to the Octopus Server. If it is not reachable, throw an error - $urlRegex = ‘([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?’ + $urlRegex = ‘([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?’ if($OctopusServerUrl -match $urlRegex){ $OctopusServerUrl = $OctopusServerUrl.Split("//") | select -Last 1 } From c417a10c372cf15edcf6521ba49374d6e54c144b Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Sat, 1 Aug 2015 13:58:19 -0500 Subject: [PATCH 06/16] Updated cTentacleAgent to extract agent installation from resource and updated ReadMe --- .../cTentacleAgent/cTentacleAgent.psm1 | 8 ++++---- README.md | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index ebf8eb33a..757747167 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -88,10 +88,10 @@ function Set-TargetResource throw "Invalid configuration: service cannot be both 'Absent' and 'Started'" } - if ( (-not $InitialDeploy) -and ($DeployProject -or $DeployVersion)) + <#if ( (-not $InitialDeploy) -and ($DeployProject -or $DeployVersion)) { throw "Invalid configuration: Resource set to not do initial deploy but Project and/or Version to deploy to specified" - } + }#> $currentResource = (Get-TargetResource -Name $Name) @@ -293,7 +293,7 @@ function New-Tentacle Write-Verbose "Beginning Tentacle installation" - $tentacleDownloadUrl = "http://octopusdeploy.com/downloads/latest/OctopusTentacle64" + <#$tentacleDownloadUrl = "http://octopusdeploy.com/downloads/latest/OctopusTentacle64" if ([IntPtr]::Size -eq 4) { $tentacleDownloadUrl = "http://octopusdeploy.com/downloads/latest/OctopusTentacle" @@ -315,7 +315,7 @@ function New-Tentacle if ($msiExitCode -ne 0) { throw "Installation of the Tentacle MSI failed; MSIEXEC exited with code: $msiExitCode. View the log at $msiLog" - } + }#> Write-Verbose "Open port $port on Windows Firewall" Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } diff --git a/README.md b/README.md index 01ce4cdae..0591dc190 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,24 @@ This repository contains a PowerShell module with a DSC resource that can be used to install the [Octopus Deploy](http://octopusdeploy.com) Tentacle agent. +## Install Tentacle Package +Installation of the MSI Package has been extracted from this resource, make use of the Package resource to install as demonstrated below + +``` +File OctopusPath{ + Ensure = "Present" + Type = "Directory" + DestinationPath = "C:\Octopus" +} +Package OctoTentacle{ + Name = "Octopus Deploy Tentacle" + Ensure = "Present" + Path = "https://download.octopusdeploy.com/octopus/Octopus.Tentacle.2.6.5.1010-x64.msi" + Arguments = "/l*v $($env:SystemDrive)\Octopus\Tentacle.msi.log" + ProductId = "" + DependsOn = @("[File]OctopusPath") +} +``` + ## Sample First, ensure the OctopusDSC module is on your `$env:PSModulePath`. Then you can create and apply configuration like this. From 777e4074a6705efb4dec46ddca9de4ec4255fa73 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Sat, 1 Aug 2015 14:15:52 -0500 Subject: [PATCH 07/16] Removed commented code --- .../cTentacleAgent/cTentacleAgent.psm1 | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 757747167..7494e2861 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -292,30 +292,6 @@ function New-Tentacle } Write-Verbose "Beginning Tentacle installation" - - <#$tentacleDownloadUrl = "http://octopusdeploy.com/downloads/latest/OctopusTentacle64" - if ([IntPtr]::Size -eq 4) - { - $tentacleDownloadUrl = "http://octopusdeploy.com/downloads/latest/OctopusTentacle" - } - - mkdir "$($env:SystemDrive)\Octopus" -ErrorAction SilentlyContinue - - $tentaclePath = "$($env:SystemDrive)\Octopus\Tentacle.msi" - if ((test-path $tentaclePath) -ne $true) - { - Write-Verbose "Downloading latest Octopus Tentacle MSI from $tentacleDownloadUrl to $tentaclePath" - Request-File $tentacleDownloadUrl $tentaclePath - } - - Write-Verbose "Installing MSI..." - $msiLog = "$($env:SystemDrive)\Octopus\Tentacle.msi.log" - $msiExitCode = (Start-Process -FilePath "msiexec.exe" -ArgumentList "/i $tentaclePath /quiet /l*v $msiLog" -Wait -Passthru).ExitCode - Write-Verbose "Tentacle MSI installer returned exit code $msiExitCode" - if ($msiExitCode -ne 0) - { - throw "Installation of the Tentacle MSI failed; MSIEXEC exited with code: $msiExitCode. View the log at $msiLog" - }#> Write-Verbose "Open port $port on Windows Firewall" Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } From b867f88877afdee1a6c9ab61e1268c79d028f906 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 28 Oct 2015 14:50:12 -0500 Subject: [PATCH 08/16] Added checks for AWS NICS so it get's the correct IP Address --- .../cTentacleAgent/cTentacleAgent.psm1 | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 7494e2861..7f74fc670 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -1,5 +1,4 @@ -function Get-TargetResource -{ +function Get-TargetResource{ [OutputType([Hashtable])] param ( [ValidateSet("Present", "Absent")] @@ -60,8 +59,7 @@ function Get-TargetResource }; } -function Set-TargetResource -{ +function Set-TargetResource{ param ( [ValidateSet("Present", "Absent")] [string]$Ensure = "Present", @@ -147,8 +145,7 @@ function Set-TargetResource Write-Verbose "Finished" } -function Test-TargetResource -{ +function Test-TargetResource{ param ( [ValidateSet("Present", "Absent")] [string]$Ensure = "Present", @@ -231,7 +228,7 @@ function Invoke-AndAssert { function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ #First Verify the adapter exists $netAdapter = Get-NetAdapter -InterfaceAlias $RegisteredNic -ErrorAction SilentlyContinue - if($netAdapter -eq $null){ + if($netAdapter -eq $null -and $RegisteredNic -ne "AWSNIC"){ throw "Selected NIC $($RegisteredNic) does not exist" } @@ -241,6 +238,9 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O $downloader = new-object System.Net.WebClient $ip = $downloader.DownloadString("http://icanhazip.com").Trim() } + elseif($RegisteredNic -eq "AWSNIC"){ + $ip = Invoke-WebRequest http://169.254.169.254/latest/meta-data/local-ipv4 | select -exp Content + } else{ Write-Verbose "Getting IP address of $($RegisteredNic) NIC" $ip = Get-NetIPAddress -InterfaceAlias $RegisteredNic -AddressFamily IPv4 | select -exp IPAddress @@ -258,10 +258,10 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O $testResult = $adapterTest.TcpTestSucceeded } else{ - $adapterTest = Test-NetConnection $OctopusServerUrl - $testResult = $adapterTest.PingSucceeded + $adapterTest = Test-NetConnection $OctopusServerUrl HTTP + $testResult = $adapterTest.TcpTestSucceeded } - if(!($testResult -and ($adapterTest.InterfaceAlias -eq $RegisteredNic))){ + if(!($testResult -and (($adapterTest.InterfaceAlias -eq $RegisteredNic) -or $RegisteredNic -eq "AWSNIC"))){ throw "Cannot reach Octopus Server $($OctopusServerUrl) from Network $($RegisteredNic). Please check your connection and try running your configuration again" } else{return $ip} From 42830dfa698992f88b022178a23b8063c0438c29 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Thu, 29 Oct 2015 08:39:29 -0500 Subject: [PATCH 09/16] Powershell version and Version increment Incremented version in psd1 to 2.20 and set powershell version to 4.0 --- OctopusDSC.psd1 | Bin 656 -> 658 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/OctopusDSC.psd1 b/OctopusDSC.psd1 index be6d55bce744f654ac3c08c48dee5bf89b2343a5..815e2f2ac155ac345f880e6203f73338ce21cadf 100644 GIT binary patch delta 22 ecmbQhI*E0H4yzG^9)r#E delta 20 ccmbQlI)Qb94znSH-bTa4jEu&UuQKih06NwND*ylh From f7a903a0c8c7916d28e88d85727fe6b58d2d4bd5 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Thu, 29 Oct 2015 14:25:32 -0500 Subject: [PATCH 10/16] Fixed agent check to look for config file in addition to reg keys --- DSCResources/cTentacleAgent/cTentacleAgent.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 7f74fc670..6e62bbbcf 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -23,7 +23,7 @@ function Get-TargetResource{ Write-Verbose "Checking if Tentacle is installed" $installLocation = (get-itemproperty -path "HKLM:\Software\Octopus\Tentacle" -ErrorAction SilentlyContinue).InstallLocation - $present = ($installLocation -ne $null) + $present = (($installLocation -ne $null) -and (Test-Path "C:\Octopus\Tentacle\Tentacle.config")) Write-Verbose "Tentacle present: $present" $currentEnsure = if ($present) { "Present" } else { "Absent" } From 51a2c6e0950238f0fcb7a98a901a3f4efd3fea4a Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Mon, 2 Nov 2015 11:06:51 -0600 Subject: [PATCH 11/16] Implemented bug fixes on IP Address Detection When scraping the metadata on ec2 instances, I had to use the WebClient method to return the IP Address if the script runs as system. I also made it possible for the script to throw an error if the function failed to grab an IP Address. ReadMe has been updated accordingly. --- .../cTentacleAgent/cTentacleAgent.psm1 | 19 ++++++++++++++----- README.md | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 6e62bbbcf..b057b1ce6 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -226,6 +226,7 @@ function Invoke-AndAssert { # needs to know the public IP address to use to connect to this Tentacle instance. Is there a way in Windows Azure in which we can # know the public IP/host name of the current machine? function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ + $downloader = new-object System.Net.WebClient #First Verify the adapter exists $netAdapter = Get-NetAdapter -InterfaceAlias $RegisteredNic -ErrorAction SilentlyContinue if($netAdapter -eq $null -and $RegisteredNic -ne "AWSNIC"){ @@ -235,12 +236,15 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O #If adapter is natted, find the actual Public IP if($isNatted){ Write-Verbose "NIC $($RegisteredNic) is natted. Determining actual public IP" - $downloader = new-object System.Net.WebClient $ip = $downloader.DownloadString("http://icanhazip.com").Trim() } + #If this is an AWS Server, using the AWSNIC as your interface name will direct this resource to scrape the machine's metadata for the local IP elseif($RegisteredNic -eq "AWSNIC"){ - $ip = Invoke-WebRequest http://169.254.169.254/latest/meta-data/local-ipv4 | select -exp Content + Write-Verbose "Getting IP Address from AWS metadata." + $ip = $downloader.DownloadString("http://169.254.169.254/latest/meta-data/local-ipv4").Trim() + Write-Verbose "Metadata returned $($ip) as this machine's IP Address." } + #Otherwise if you know the name of your network interface, this resource will get the IP of the interface that matches the interface name you provided. else{ Write-Verbose "Getting IP address of $($RegisteredNic) NIC" $ip = Get-NetIPAddress -InterfaceAlias $RegisteredNic -AddressFamily IPv4 | select -exp IPAddress @@ -254,13 +258,14 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O if($OctopusServerUrl.Contains(":")){ $port = $OctopusServerUrl.Split(":")[1] $OctopusServerUrl = $OctopusServerUrl.Split(":")[0] - $adapterTest = Test-NetConnection $OctopusServerUrl -port $port + $adapterTest = Test-NetConnection $OctopusServerUrl -port $port -InformationLevel Detailed $testResult = $adapterTest.TcpTestSucceeded } else{ - $adapterTest = Test-NetConnection $OctopusServerUrl HTTP + $adapterTest = Test-NetConnection $OctopusServerUrl HTTP -InformationLevel Detailed $testResult = $adapterTest.TcpTestSucceeded } + Write-Verbose "The connection test to $($OctopusServerUrl) from $($ip) using the $($RegisteredNic) interface returned: $($testResult)." if(!($testResult -and (($adapterTest.InterfaceAlias -eq $RegisteredNic) -or $RegisteredNic -eq "AWSNIC"))){ throw "Cannot reach Octopus Server $($OctopusServerUrl) from Network $($RegisteredNic). Please check your connection and try running your configuration again" } @@ -297,8 +302,12 @@ function New-Tentacle Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } $ipAddress = Get-MyPublicIPAddress -RegisteredNic $RegisteredNic -isNatted $isNatted -OctopusServerUrl $octopusServerUrl + + if($ipAddress -eq $null){ + throw "I don't have an IP Address to register with" + } - Write-Verbose "Public IP address: $ipAddress" + Write-Verbose "Public IP address: $($ipAddress)" Write-Verbose "Configuring and registering Tentacle" pushd "${env:ProgramFiles}\Octopus Deploy\Tentacle" diff --git a/README.md b/README.md index 0591dc190..8d42bcb9f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Configuration SampleConfig # Optional settings ListenPort = 10933 - RegisteredNic = "Public" + RegisteredNic = "Public" #Use interface name here if you know the name of the interface. If this is an AWS ec2 instance, use AWSNIC as the value and the resource will scrape the machine's metadata isNatted = $false DefaultApplicationDirectory = "C:\Octopus" } From 98374217be25ef427d202992af47f29a33de554c Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Tue, 24 Nov 2015 16:30:21 -0600 Subject: [PATCH 12/16] Format Cleanup and regex update --- .../cTentacleAgent/cTentacleAgent.psm1 | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index b057b1ce6..ea2727de5 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -186,8 +186,7 @@ function Test-TargetResource{ return $true } -function Get-TentacleServiceName -{ +function Get-TentacleServiceName{ param ( [string]$instanceName ) if ($instanceName -eq "Tentacle") @@ -200,8 +199,7 @@ function Get-TentacleServiceName } } -function Request-File -{ +function Request-File{ param ( [string]$url, [string]$saveAs @@ -212,7 +210,7 @@ function Request-File $downloader.DownloadFile($url, $saveAs) } -function Invoke-AndAssert { +function Invoke-AndAssert{ param ($block) & $block | Write-Verbose @@ -225,7 +223,8 @@ function Invoke-AndAssert { # After the Tentacle is registered with Octopus, Tentacle listens on a TCP port, and Octopus connects to it. The Octopus server # needs to know the public IP address to use to connect to this Tentacle instance. Is there a way in Windows Azure in which we can # know the public IP/host name of the current machine? -function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ +function Get-MyPublicIPAddress([string]$nicType,[string]$nicName,[string]$OctopusServerUrl){ + $downloader = new-object System.Net.WebClient #First Verify the adapter exists $netAdapter = Get-NetAdapter -InterfaceAlias $RegisteredNic -ErrorAction SilentlyContinue @@ -251,7 +250,8 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O } #Test the connection to the Octopus Server. If it is not reachable, throw an error - $urlRegex = ‘([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?’ + #$urlRegex = �([a-zA-Z]{3,})://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)*?� + $urlRegex = "(http[s]?|[s]?ftp[s]?)(:\/\/)([^\s,]+)" if($OctopusServerUrl -match $urlRegex){ $OctopusServerUrl = $OctopusServerUrl.Split("//") | select -Last 1 } @@ -272,8 +272,7 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O else{return $ip} } -function New-Tentacle -{ +function New-Tentacle{ param ( [Parameter(Mandatory=$True)] [string]$name, @@ -301,8 +300,13 @@ function New-Tentacle Write-Verbose "Open port $port on Windows Firewall" Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } - $ipAddress = Get-MyPublicIPAddress -RegisteredNic $RegisteredNic -isNatted $isNatted -OctopusServerUrl $octopusServerUrl - + if(($nicType -eq "named") -and ($nicName -ne $null)){ + $ipAddress = Get-MyPublicIPAddress -nicType $nicType -nicName $nicName -OctopusServerUrl $octopusServerUrl + } + else{ + $ipAddress = Get-MyPublicIPAddress -nicType $nicType -OctopusServerUrl $octopusServerUrl + } + if($ipAddress -eq $null){ throw "I don't have an IP Address to register with" } @@ -349,8 +353,7 @@ function New-Tentacle } -function Remove-TentacleRegistration -{ +function Remove-TentacleRegistration{ param ( [Parameter(Mandatory=$True)] [string]$name, From 92b77e2ddc5ab5cc25e533ddfac39b9ee0e75b60 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Tue, 24 Nov 2015 16:32:58 -0600 Subject: [PATCH 13/16] Forgot to correct the parameters accepted --- DSCResources/cTentacleAgent/cTentacleAgent.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index ea2727de5..0552f0782 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -223,7 +223,7 @@ function Invoke-AndAssert{ # After the Tentacle is registered with Octopus, Tentacle listens on a TCP port, and Octopus connects to it. The Octopus server # needs to know the public IP address to use to connect to this Tentacle instance. Is there a way in Windows Azure in which we can # know the public IP/host name of the current machine? -function Get-MyPublicIPAddress([string]$nicType,[string]$nicName,[string]$OctopusServerUrl){ +function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ $downloader = new-object System.Net.WebClient #First Verify the adapter exists From 958b4cdb22721c81ded35a5e853b1cd529eb1fc5 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Tue, 24 Nov 2015 18:10:32 -0600 Subject: [PATCH 14/16] More format cleanup and a new streamlined process registration IP detection --- .../cTentacleAgent/cTentacleAgent.psm1 | 102 ++++++++++++++---- .../cTentacleAgent/cTentacleAgent.schema.mof | 4 +- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 0552f0782..1a88f4ddb 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -17,13 +17,16 @@ function Get-TargetResource{ [string[]]$Roles, [string]$DefaultApplicationDirectory, [int]$ListenPort, - [string]$RegisteredNic, - [bool]$isNatted=$false + [ValidateSet("named","detect","natted")] + $nicType="detect", + $nicName=$null + <#[string]$RegisteredNic, + [bool]$isNatted=$false#> ) Write-Verbose "Checking if Tentacle is installed" $installLocation = (get-itemproperty -path "HKLM:\Software\Octopus\Tentacle" -ErrorAction SilentlyContinue).InstallLocation - $present = (($installLocation -ne $null) -and (Test-Path "C:\Octopus\Tentacle\Tentacle.config")) + $present = (($installLocation -ne $null) -and (Test-Path "C:\Octopus\$($Name)\Tentacle.config")) Write-Verbose "Tentacle present: $present" $currentEnsure = if ($present) { "Present" } else { "Absent" } @@ -77,8 +80,11 @@ function Set-TargetResource{ [string[]]$Roles, [string]$DefaultApplicationDirectory = "$($env:SystemDrive)\Applications", [int]$ListenPort = 10933, - [string]$RegisteredNic, - [bool]$isNatted=$false + [ValidateSet("named","detect","natted")] + $nicType="detect", + $nicName=$null + <#[string]$RegisteredNic, + [bool]$isNatted=$false#> ) if ($Ensure -eq "Absent" -and $State -eq "Started") @@ -131,7 +137,7 @@ function Set-TargetResource{ elseif ($Ensure -eq "Present" -and $currentResource["Ensure"] -eq "Absent") { Write-Verbose "Installing Tentacle..." - New-Tentacle -name $Name -apiKey $ApiKey -octopusServerUrl $OctopusServerUrl -port $ListenPort -RegisteredNic $RegisteredNic -isNatted $isNatted -environments $Environments -roles $Roles -DefaultApplicationDirectory $DefaultApplicationDirectory + New-Tentacle -name $Name -apiKey $ApiKey -octopusServerUrl $OctopusServerUrl -port $ListenPort -nicType $nicType -nicName $nicName -environments $Environments -roles $Roles -DefaultApplicationDirectory $DefaultApplicationDirectory Write-Verbose "Tentacle installed!" } @@ -163,8 +169,11 @@ function Test-TargetResource{ [string[]]$Roles, [string]$DefaultApplicationDirectory, [int]$ListenPort, - [string]$RegisteredNic, - [bool]$isNatted=$false + [ValidateSet("named","detect","natted")] + $nicType="detect", + $nicName=$null + <#[string]$RegisteredNic, + [bool]$isNatted=$false#> ) $currentResource = (Get-TargetResource -Name $Name) @@ -223,7 +232,64 @@ function Invoke-AndAssert{ # After the Tentacle is registered with Octopus, Tentacle listens on a TCP port, and Octopus connects to it. The Octopus server # needs to know the public IP address to use to connect to this Tentacle instance. Is there a way in Windows Azure in which we can # know the public IP/host name of the current machine? -function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ +function VerifyConnection{ + param( + $serverAddress, + $port + ) + + Return Test-NetConnection $serverAddress -Port $port -InformationLevel Detailed -Verbose +} +function Get-RegistrationIP{ + param( + $nicType, + $nicName, + $url + ) + + $urlRegex = "(http[s]?|[s]?ftp[s]?)(:\/\/)([^\s,]+)"; $port = $null; $ipAddress = $null + if($url -match $urlRegex){ + $useHttps = ($url.Split("//") | select -First 1) -eq "https:" + $serverAddress = $url.Split("//") | select -Last 1 + } + else{$serverAddress = $url} + if($serverAddress.Contains(":")){ + $port = $serverAddress.Split(":")[1] + $serverAddress = $serverAddress.Split(":")[0] + } + if($useHttps -and ($port -eq $null)){$port = 443} + elseif($port -eq $null){$port = 80} + Write-Verbose "Connection tests will use address $($serverAddress) on port $($port)" + + switch($nicType){ + "named"{ + $netAdapter = Get-NetAdapter -InterfaceAlias $nicName -ErrorAction SilentlyContinue + if(($netAdapter -eq $null) -or ($netAdapter.Status -ne "Up")){ + throw "Selected NIC $($nicName) does not exist or does not have a connection to the network." + } + $testResult = VerifyConnection $serverAddress $port + if($testResult.TcpTestSucceeded -and ($testResult.InterfaceAlias -eq $netAdapter.Name)){ + $ipAddress = (Get-NetIPAddress -InterfaceAlias $netAdapter.Name -AddressFamily IPv4 -SkipAsSource $false -ErrorAction SilentlyContinue).IPv4Address + } + } + "detect"{ + $testResult = VerifyConnection $serverAddress $port + if($testResult.TcpTestSucceeded){ + $ipAddress = $testResult.SourceAddress.IPv4Address + } + } + "natted"{ + $testResult = VerifyConnection $serverAddress $port + if($testResult.TcpTestSucceeded){ + $downloader = new-object System.Net.WebClient + $ipAddress = $downloader.DownloadString("http://icanhazip.com").Trim() + } + } + } + + Return $ipAddress +} +<#function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$OctopusServerUrl){ $downloader = new-object System.Net.WebClient #First Verify the adapter exists @@ -270,7 +336,7 @@ function Get-MyPublicIPAddress([string]$RegisteredNic,[bool]$isNatted,[string]$O throw "Cannot reach Octopus Server $($OctopusServerUrl) from Network $($RegisteredNic). Please check your connection and try running your configuration again" } else{return $ip} -} +}#> function New-Tentacle{ param ( @@ -285,8 +351,11 @@ function New-Tentacle{ [Parameter(Mandatory=$True)] [string[]]$roles, [int] $port, - [string]$RegisteredNic, - [bool]$isNatted=$false, + [ValidateSet("named","detect","natted")] + $nicType="detect", + $nicName=$null, + #[string]$RegisteredNic, + #[bool]$isNatted=$false, [string]$DefaultApplicationDirectory ) @@ -300,13 +369,8 @@ function New-Tentacle{ Write-Verbose "Open port $port on Windows Firewall" Invoke-AndAssert { & netsh.exe advfirewall firewall add rule protocol=TCP dir=in localport=$port action=allow name="Octopus Tentacle: $Name" } - if(($nicType -eq "named") -and ($nicName -ne $null)){ - $ipAddress = Get-MyPublicIPAddress -nicType $nicType -nicName $nicName -OctopusServerUrl $octopusServerUrl - } - else{ - $ipAddress = Get-MyPublicIPAddress -nicType $nicType -OctopusServerUrl $octopusServerUrl - } - + #$ipAddress = Get-MyPublicIPAddress -RegisteredNic $RegisteredNic -isNatted $isNatted -OctopusServerUrl $octopusServerUrl + $ipAddress = Get-RegistrationIP -nicType $nicType -nicName $nicName -url $octopusServerUrl if($ipAddress -eq $null){ throw "I don't have an IP Address to register with" } diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.schema.mof b/DSCResources/cTentacleAgent/cTentacleAgent.schema.mof index b0db70f68..0abb63038 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.schema.mof +++ b/DSCResources/cTentacleAgent/cTentacleAgent.schema.mof @@ -10,7 +10,7 @@ class cTentacleAgent : OMI_BaseResource [Write] string Environments[]; [Write] string Roles[]; [Write] UInt32 ListenPort; - [Write] string RegisteredNic; - [Write] boolean isNatted; + [Write] string nicType; + [Write] boolean nicName; [Write] string DefaultApplicationDirectory; }; From ecd19404b96ae6d6443b87d89312dd6245884dc5 Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Tue, 24 Nov 2015 18:16:50 -0600 Subject: [PATCH 15/16] ReadMe update with new usage info --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8d42bcb9f..1cb25fc5e 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ Configuration SampleConfig # Optional settings ListenPort = 10933 - RegisteredNic = "Public" #Use interface name here if you know the name of the interface. If this is an AWS ec2 instance, use AWSNIC as the value and the resource will scrape the machine's metadata - isNatted = $false + nicType = "detect" #This sets the value for what kind of NIC we should expect. "detect" will test the connection to the octopus server and use the NIC that successfully connects, it is the default value. "named" is where you know the name of the NIC and the connection will be tested and verify the NIC named is the one that reached the octopus server. "nicName" variable is required if this is set. "natted" will verify a connection to the octopus server and then it will call a 3rd party service to determine it's public IP + nicName #This is only required if nicType is set to "named" DefaultApplicationDirectory = "C:\Octopus" } } From 9222a89455ea5c5766b2be536c763a7e49b56f8a Mon Sep 17 00:00:00 2001 From: Brandon VanDermark Date: Wed, 25 Nov 2015 19:52:34 -0600 Subject: [PATCH 16/16] Format standardization --- .../cTentacleAgent/cTentacleAgent.psm1 | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 index 1a88f4ddb..2e5217e4c 100644 --- a/DSCResources/cTentacleAgent/cTentacleAgent.psm1 +++ b/DSCResources/cTentacleAgent/cTentacleAgent.psm1 @@ -359,8 +359,7 @@ function New-Tentacle{ [string]$DefaultApplicationDirectory ) - if ($port -eq 0) - { + if ($port -eq 0){ $port = 10933 } @@ -392,18 +391,14 @@ function New-Tentacle{ $registerArguments = @("register-with", "--instance", $name, "--server", $octopusServerUrl, "--name", $env:COMPUTERNAME, "--publicHostName", $ipAddress, "--apiKey", $apiKey, "--comms-style", "TentaclePassive", "--force", "--console") - foreach ($environment in $environments) - { - foreach ($e2 in $environment.Split(',')) - { + foreach ($environment in $environments){ + foreach ($e2 in $environment.Split(',')){ $registerArguments += "--environment" $registerArguments += $e2.Trim() } } - foreach ($role in $roles) - { - foreach ($r2 in $role.Split(',')) - { + foreach ($role in $roles){ + foreach ($r2 in $role.Split(',')){ $registerArguments += "--role" $registerArguments += $r2.Trim() } @@ -428,16 +423,14 @@ function Remove-TentacleRegistration{ ) $tentacleDir = "${env:ProgramFiles}\Octopus Deploy\Tentacle" - if ((test-path $tentacleDir) -and (test-path "$tentacleDir\tentacle.exe")) - { + if ((test-path $tentacleDir) -and (test-path "$tentacleDir\tentacle.exe")){ Write-Verbose "Beginning Tentacle deregistration" Write-Verbose "Tentacle commands complete" pushd $tentacleDir Invoke-AndAssert { & .\tentacle.exe deregister-from --instance "$name" --server $octopusServerUrl --apiKey $apiKey --console } popd } - else - { + else{ Write-Verbose "Could not find Tentacle.exe" } -} +} \ No newline at end of file