Cluster.psm1
$ErrorActionPreference = "Stop" $InformationPreference = "Continue" <## # New cluster set #> function New-ClusterService { <# .SYNOPSIS Creates a new Service in Azure and returns the associated ClusterService object .DESCRIPTION Creates a new Service in Azure and returns the associated ClusterService object .PARAMETER Name Name of the Service .EXAMPLE New-ClusterService -Name "MyService" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [ValidatePattern("^[A-Z][A-z0-9]+$")] [Alias('ServiceName')] [string]$Name ) $service = [ClusterService]::new($Name) $service.Create() return $service } function New-ClusterFlightingRing { <# .SYNOPSIS Creates a new Flighting Ring in Azure and returns the associated ClusterFlightingRing object .DESCRIPTION Creates a new Flighting Ring in Azure and returns the associated ClusterFlightingRing object .PARAMETER ServiceName Name of the Service containg the Flighting Ring .PARAMETER Service ClusterService object of the Service containing the Flighting Ring .PARAMETER Name Name of the Flighting Ring .EXAMPLE # create "MyService-DEV" using names New-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" # create "MyService-DEV" using management objects $service = Get-ClusterService -Name "MyService" New-ClusterFlightingRing -Service $service -Name "DEV" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidateNotNullOrEmpty()] [ClusterService]$Service, [Parameter(Mandatory, ParameterSetName = 'Components')] [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidatePattern("^[A-Z]{3,6}$")] [Alias('FlightingRingName')] [string]$Name ) $id = switch ($PSCmdlet.ParameterSetName) { "Components" {"$ServiceName-$Name"} "Object" {"$Service-$Name"} } $flightingRing = [ClusterFlightingRing]::new($id) $flightingRing.Create() return $flightingRing } function New-ClusterEnvironment { <# .SYNOPSIS Creates a new Environment in Azure and returns the associated ClusterEnvironment object .DESCRIPTION Creates a new Environment in Azure and returns the associated ClusterEnvironment object .PARAMETER ServiceName Name of the Service containing the Environment .PARAMETER FlightingRingName Name of the Flighting Ring containing the Environment .PARAMETER FlightingRing ClusterFlightingRing object of the Flighting Ring containing the Environment .PARAMETER Region Name of the Region containing the Environment .EXAMPLE # create "MyService-DEV-EastUS" using names New-ClusterEnvironment -ServiceName "MyService" -FlightingRingName "DEV" -RegionName "EastUS" # create "MyService-DEV-EastUS" using management objects $flightingRing = Get-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" New-ClusterEnvironment -FlightingRing $flightingRing -Region "EastUS" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z]{3,6}$")] [string]$FlightingRingName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidateNotNullOrEmpty()] [ClusterFlightingRing]$FlightingRing, [Parameter(Mandatory, ParameterSetName = 'Components')] [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidatePattern("^[A-z][A-z0-9 ]+$")] [Alias('RegionName')] [string]$Region ) $id = switch ($PSCmdlet.ParameterSetName) { "Components" {"$ServiceName-$FlightingRingName-$Region"} "Object" {"$FlightingRing-$Region"} } $environment = [ClusterEnvironment]::new($id) $environment.Create() return $environment } function New-Cluster { <# .SYNOPSIS Creates a new Cluster in Azure and returns the associated Cluster object .DESCRIPTION Creates a new Cluster in Azure and returns the associated Cluster object .PARAMETER ServiceName Name of the Service containing the Cluster .PARAMETER FlightingRingName Name of the Flighting Ring containing the Cluster .PARAMETER RegionName Name of the Region containing the Cluster .PARAMETER Environment ClusterEnvironment object of the Flighting Ring containing the Cluster .PARAMETER DefinitionsContainer Path to the folder containing all the configuration definitions .PARAMETER Expiry Date when the configuration can no longer be read from Azure without redeploying .EXAMPLE # create cluster child of "MyService-DEV-EastUS" using names New-Cluster -ServiceName "MyService" -FlightingRingName "DEV" -RegionName "EastUS" # create "MyService-DEV-EastUS" using management objects $environment = Get-ClusterEnvironment -ServiceName "MyService" -FlightingRingName "DEV" -RegionName "EastUS" New-Cluster -Environment $environment -DefinitionsContainer ".\Definitions" -Expiry (Get-Date).AddDays(14) .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z]{3,6}$")] [string]$FlightingRingName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-z][A-z0-9 ]+$")] [Alias('Region')] [string]$RegionName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidateNotNullOrEmpty()] [ClusterEnvironment]$Environment, [ValidateScript( {Test-Path $_} )] [string]$DefinitionsContainer = (Resolve-Path "."), [ValidateNotNullOrEmpty()] [datetime]$Expiry = [datetime]::MaxValue ) if (-not $Environment) { $Environment = [ClusterEnvironment]::new("$ServiceName-$FlightingRingName-$RegionName") } $cluster = $Environment.NewChildCluster() $cluster.PublishConfiguration($DefinitionsContainer, $Expiry) | Write-Log return $cluster } <## # Get cluster set #> function Get-ClusterService { <# .SYNOPSIS Gets the ClusterService object .DESCRIPTION Gets the ClusterService object .PARAMETER Name Name of the Service .EXAMPLE $service = Get-ClusterService -Name "MyService" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [Alias('ServiceName')] [string]$Name ) return [ClusterService]::new($Name) } function Get-ClusterFlightingRing { <# .SYNOPSIS Gets the ClusterFlightingRing object .DESCRIPTION Gets the ClusterFlightingRing object .PARAMETER ServiceName Name of the Service containing the Flighting Ring .PARAMETER Service ClusterService object of the Service containing the Flighting Ring .PARAMETER Name Name of the Flighting Ring .EXAMPLE $flightingRing = Get-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ClusterService]$Service, [Parameter(Mandatory, ParameterSetName = 'Components')] [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidatePattern("^[A-Z]{3,6}$")] [Alias('FlightingRingName')] [string]$Name ) $id = switch ($PSCmdlet.ParameterSetName) { "Components" {"$ServiceName-$Name"} "Object" {"$Service-$Name"} } return [ClusterFlightingRing]::new($id) } function Get-ClusterEnvironment { <# .SYNOPSIS Gets the ClusterEnvironment object .DESCRIPTION Gets the ClusterEnvironment object .PARAMETER ServiceName Name of the Service containing the Environment .PARAMETER FlightingRingName Name of the Flighting Ring containing the Environment .PARAMETER FlightingRing ClusterFlightingRing object of the Flighting Ring containing the Environment .PARAMETER Region Name of the Region containing the Environment .EXAMPLE $environment = Get-ClusterEnvironment -ServiceName "MyService" -FlightingRingName "DEV" -RegionName "EastUS" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z]{3,6}$")] [string]$FlightingRingName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ClusterFlightingRing]$FlightingRing, [Parameter(Mandatory, ParameterSetName = 'Components')] [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidatePattern("^[A-z][A-z0-9 ]+$")] [Alias('RegionName')] [string]$Region ) $id = switch ($PSCmdlet.ParameterSetName) { "Components" {"$ServiceName-$FlightingRingName-$Region"} "Object" {"$FlightingRing-$Region"} } return [ClusterEnvironment]::new($id) } function Get-Cluster { <# .SYNOPSIS Gets the Cluster object .DESCRIPTION Gets the Cluster object .PARAMETER ServiceName Name of the Service containing the Cluster .PARAMETER FlightingRingName Name of the Flighting Ring containing the Cluster .PARAMETER RegionName Name of the Region containing the Cluster .PARAMETER Environment ClusterEnvironment object of the Flighting Ring containing the Cluster .PARAMETER Index Index of the Cluster within its Environment .EXAMPLE $cluster = Get-Cluster -ServiceName "MyService" -FlightingRingName "DEV" -RegionName "EastUS" -Index 0 .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z][A-z0-9]+$")] [string]$ServiceName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-Z]{3,6}$")] [string]$FlightingRingName, [Parameter(Mandatory, ParameterSetName = 'Components')] [ValidatePattern("^[A-z][A-z0-9 ]+$")] [Alias('Region')] [string]$RegionName, [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidateNotNullOrEmpty()] [ClusterEnvironment]$Environment, [Parameter(Mandatory, ParameterSetName = 'Components')] [Parameter(Mandatory, ParameterSetName = 'Object')] [ValidateRange(0, 255)] [int]$Index ) $id = switch ($PSCmdlet.ParameterSetName) { "Components" {"$ServiceName-$FlightingRingName-$RegionName-$Index"} "Object" {"$Environment-$Index"} } return [Cluster]::new($id) } <## # Publish to cluster set #> function Publish-ClusterArtifact { <# .SYNOPSIS Uploads an artifact to the specified ClusterSet .DESCRIPTION Uploads the artifact to the specified Cluster set and stores it as its name in the "artifacts" container .PARAMETER ClusterSet The Cluster management object representing the subtree of the service that will hold the secret .PARAMETER Path Local path to the artifact to be uploaded .EXAMPLE $flightingRing = Get-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" Publish-ClusterArtifact -ClusterSet $flightingRing -Path ".\DefinitionsContainer" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ClusterResourceGroup]$ClusterSet, [Parameter(Mandatory)] [ValidateScript( {Test-Path $_} )] [string]$Path ) $ClusterSet.UploadArtifact($Path) $ClusterSet.PropagateArtifacts() } function Publish-ClusterSecret { <# .SYNOPSIS Creates a new secret .DESCRIPTION Creates a new Key Vault secret in the specified ClusterSet .PARAMETER ClusterSet The Cluster management object representing the subtree of the service that will hold the secret .PARAMETER Name Name of the secret .PARAMETER Value Value of the secret, represented as a Secure String .PARAMETER ContentType MIME type of the secret .EXAMPLE $flightingRing = Get-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" $secretName = "MySecret" $secretValue = Read-Host "Enter value for '$secretName' in this secure prompt" -AsSecureString Publish-ClusterSecret -ClusterSet $flightingRing -Name $secretName -Value $secretValue .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ClusterResourceGroup]$ClusterSet, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [SecureString]$Value, [string]$ContentType = "text/plain" ) Set-AzureKeyVaultSecret ` -VaultName (Get-AzureRmKeyVault -ResourceGroupName $ClusterSet).VaultName ` -ContentType $ContentType ` -Name $Name ` -SecretValue $Value $ClusterSet.PropagateSecrets() } function Publish-ClusterImage { <# .SYNOPSIS Creates a new baked image .DESCRIPTION Captures a generalized VM image containing the latest Windows Updates and the specified Windows Features .PARAMETER ClusterSet The Cluster management object representing the subtree of the service that will hold the image .PARAMETER WindowsFeature List of Windows Features to be baked into the custom Windows Image .EXAMPLE $flightingRing = Get-ClusterFlightingRing -ServiceName "MyService" -FlightingRingName "DEV" Publish-ClusterImage -ClusterSet $flightingRing -WindowsFeature "Web-Server", "Web-Asp-Net45", "Telnet-Client" .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [ClusterResourceGroup]$ClusterSet, [string[]]$WindowsFeature = @() ) $ClusterSet.NewImage($WindowsFeature) $ClusterSet.PropagateImages() } function Publish-ClusterConfiguration { <# .SYNOPSIS Pushes a new Resource Manager Template configuration to the Cluster resource group .DESCRIPTION Creates a new Azure Resource Manager Template Deployment, which will ensure the Cluster reflects the template and trigger any Desired State Configuration extensions or Custom Script Extensions in the script. .PARAMETER Cluster The Cluster(s) that will be updated with their new Configurations .PARAMETER DefinitionsContainer Path to the folder containing all the configuration definitions .PARAMETER Expiry Date when the configuration can no longer be read from Azure without redeploying .EXAMPLE $clusters = Select-Cluster "MyService" "DEV" "EastUS" Publish-ClusterConfiguration -Cluster $clusters -DefinitionsContainer ".\Definitions" -Expiry (Get-Date).AddDays(14) .NOTES Must be logged into Azure #> Param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [Cluster[]]$Cluster, [ValidateScript( {Test-Path $_} )] [string]$DefinitionsContainer = (Resolve-Path "."), [ValidateNotNullOrEmpty()] [datetime]$Expiry = [datetime]::MaxValue ) $Cluster.PublishConfiguration($DefinitionsContainer, $Expiry) | Write-Log } <## # Utilitiies #> function Select-Cluster { <# .SYNOPSIS Returns an array of Cluster objects matching the parameters .DESCRIPTION Queries the current Azure subscription for .PARAMETER ServiceName Name (or glob pattern) of the Service containing the Cluster .PARAMETER FlightingRingName Name (or glob pattern) of the Flighting Ring containing the Cluster .PARAMETER RegionName Name (or glob pattern) of the Region containing the Cluster .PARAMETER Index Index (or glob pattern) of the Cluster .EXAMPLE $clusters = Select-Cluster "MyService" "DEV" "EastUS" .NOTES Must be logged into Azure #> Param( [Alias('Service')] [string]$ServiceName = "*", [Alias('FlightingRing')] [string]$FlightingRingName = "*", [Alias('Region')] [string]$RegionName = "*", [string]$Index = "*" ) $query = "$ServiceName-$FlightingRingName-$RegionName-$Index" return Get-AzureRmResourceGroup ` | ? {$_.ResourceGroupName -like $query} ` | % {[Cluster]::new($_.ResourceGroupName)} } |