Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save jeremiahredekop/1b74cb99e3804392fe4c to your computer and use it in GitHub Desktop.

Select an option

Save jeremiahredekop/1b74cb99e3804392fe4c to your computer and use it in GitHub Desktop.

Revisions

  1. jeremiahredekop revised this gist Mar 25, 2015. 1 changed file with 102 additions and 0 deletions.
    102 changes: 102 additions & 0 deletions instructions.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    Taken from (https://groups.google.com/forum/#!searchin/event-store/azure/event-store/SBgFKYLdGaw/U-YBI_O9iZkJ)

    I thought I would share the work I have done to automatically setup Event Store running on Azure VMs. Right now, it only targets Windows hosts, but should be easily extended for Linux. To extend for Linux, you would need to create a shell script to replace the PowerShell provisioning script shown in step #4. I have observed it takes about 20 minutes until all the VMs are running with Event Store.

    https://gist.github.com/pbolduc/f8ba49358a97e1e95332

    Files:

    ProvisionEventStore.ps1 - creates Affinity Group, Storage Account, VMs and data disks for VMs
    EventStoreScriptExtensionProvisionFile.ps1 - Run automatically on the VM to install and configure Event Store to run as a service using NSSM

    Features:

    Creates any number of VMs for your cluster. No validation to ensure the number is odd. See -ClusterSize
    Creates a stripped data disk using as many data disks the VM will support based on the instance size. The user specifies total target disk size in GB
    Creates all VMs inside the same cloud service. VMs in the same cloud service can resolve the VM names to internal IP addresses
    Uses a virtual network so nodes can user internal addresses for communication
    Sets up all resources in one affinity group to ensure VMs and storage are close to each other in the data center
    Creates a random storage account name to avoid conflicts (uses the user supplied prefix)

    on each VM created:

    Formats all the data disks into a single striped volume
    Installs Chocolatey
    Installs NSSM using Chocolatey
    Downloads Event Store 3.0.1 from http://download.geteventstore.com/binaries/EventStore-OSS-Win-v3.0.1.zip
    Determines the IP addresses of the other nodes and configures the gossip seeds in the configuration file
    Adds a service called 'EventStore' that will start automatically
    Logs are written to D:\Logs\eventstore\
    Data is stored to F:\Data\eventstore\
    Adds firewall rules to allow Event Store traffic
    Adds netsh urlacls for Event Store


    How to use:

    1. Manually create a virtual network so that your Event Store nodes can talk to each other on private IP addresses
    2. Manually create a named subnet in your virtual network
    3. Manually create a storage account and container to host the the custom script extension
    4. Upload file EventStoreScriptExtensionProvisionFile.ps1 (found in the gist) to your custom script extension container
    5. Install the Azure PowerShell Cmdlets and ensure they are working with your subscription (see: How to install and configure Azure PowerShell)
    6. Login to your Azure account using Add-AzureAccount and/or
    7. Run ProvisionEventStore.ps1 with your desired parameters

    Example Execution:

    # Run Add-AzureAccount to get a authorization token

    $VerbosePreference = 'Continue'

    Write-Verbose "$(Get-Date -Format 'T') Starting Provision Environment"

    . "$PSScriptRoot\ProvisionEventStore.ps1" `
    -ClusterSize 3 `
    -DataDiskSize 160 `
    -Location "West US" `
    -InstanceSize "Medium" `
    -username "admin-username" `
    -password "admin-password" `
    -ServiceName "cloud-service-name" `
    -VMName "vm-name-prefix" `
    -ImageName "a699494373c04fc0bc8f2bb1389d6106__Windows-Server-2012-R2-201502.01-en.us-127GB.vhd" `
    -AffinityGroup "affinity-group-name" `
    -TargetStorageAccountName "target-storage-account" `
    -AvailabilitySetName "availability-set-name" `
    -VNetName "virtual-network-name" `
    -VNetSubnetName "subnet-name" `
    -CustomScriptExtensionStorageAccountName "storage-account-name" `
    -CustomScriptExtensionStorageAccountKey 'storage-account-key' `
    -CustomScriptExtensionContainerName 'storage-account-container-name' `
    -CustomScriptExtensionProvisionFile 'EventStoreScriptExtensionProvisionFile.ps1'

    Write-Verbose "$(Get-Date -Format 'T') Provision Complete"


    Example output:

    VERBOSE: 1:54:35 PM Starting Provision Environment
    VERBOSE: 1:54:35 PM Ensuring Affinity Group 'EventStore' exists and is in 'West US' location.
    VERBOSE: 1:54:35 PM - Begin Operation: Get-AzureAffinityGroup
    VERBOSE: 1:54:36 PM - Completed Operation: Get-AzureAffinityGroup
    VERBOSE: 1:54:36 PM - Begin Operation: Get-AzureStorageAccount
    VERBOSE: 1:54:37 PM - Completed Operation: Get-AzureStorageAccount
    WARNING: GeoReplicationEnabled property will be deprecated in a future release of Azure PowerShell. The value will be merged into the AccountType property.
    VERBOSE: 1:54:37 PM - Begin Operation: New-AzureStorageAccount
    VERBOSE: 1:55:09 PM - Completed Operation: New-AzureStorageAccount
    VERBOSE: 1:55:09 PM Waiting for storage account eventstoreaeugnjsexgyefy to be available...
    VERBOSE: 1:55:09 PM - Begin Operation: Get-AzureStorageAccount
    VERBOSE: 1:55:10 PM - Completed Operation: Get-AzureStorageAccount
    WARNING: GeoReplicationEnabled property will be deprecated in a future release of Azure PowerShell. The value will be merged into the AccountType property.
    VERBOSE: 1:55:12 PM Creating Virtual Machines
    VERBOSE: 1:55:12 PM - Begin Operation: New-AzureService
    VERBOSE: 1:55:14 PM - Completed Operation: New-AzureService
    VERBOSE: 1:55:14 PM - Begin Operation: Get-AzureRoleSize
    VERBOSE: 1:55:14 PM - Completed Operation: Get-AzureRoleSize
    VERBOSE: 1:55:28 PM - Begin Operation: New-AzureVM - Create Deployment with VM ES-demo-1
    VERBOSE: 1:56:40 PM - Completed Operation: New-AzureVM - Create Deployment with VM ES-demo-1
    VERBOSE: 1:56:40 PM - Begin Operation: New-AzureVM - Create VM ES-demo-2
    VERBOSE: 1:57:47 PM - Completed Operation: New-AzureVM - Create VM ES-demo-2
    VERBOSE: 1:57:47 PM - Begin Operation: New-AzureVM - Create VM ES-demo-3
    VERBOSE: 1:58:53 PM - Completed Operation: New-AzureVM - Create VM ES-demo-3
    VERBOSE: 1:58:53 PM Provision Complete
  2. @pbolduc pbolduc revised this gist Mar 10, 2015. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions EventStoreScriptExtensionProvisionFile.ps1
    Original file line number Diff line number Diff line change
    @@ -126,8 +126,8 @@ function New-EventStoreConfigFile() {
    Format-DataDisks

    Install-Chocolatey -InstallToPath "C:\Chocolatey"
    Download-EventStore -DownloadUrl "http://download.geteventstore.com/binaries/EventStore-OSS-Win-v3.0.1.zip" -SaveToPath "D:\EventStore-OSS-Win-v3.0.1.zip"
    Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.1.zip" -destination "C:\EventStore\v3.0.1\"
    Download-EventStore -DownloadUrl "http://download.geteventstore.com/binaries/EventStore-OSS-Win-v3.0.2.zip" -SaveToPath "D:\EventStore-OSS-Win-v3.0.2.zip"
    Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.2.zip" -destination "C:\EventStore\v3.0.2\"

    New-EventStoreConfigFile

    @@ -156,7 +156,7 @@ netsh http add urlacl url=http://${ipAddress}:${ExtHttpPort}/ user="NT AUTHORITY
    New-NetFirewallRule -Name Allow_EventStore_Int_In -DisplayName "Allow inbound Internal Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort ${IntTcpPort},${IntHttpPort}
    New-NetFirewallRule -Name Allow_EventStore_Ext_In -DisplayName "Allow inbound External Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort ${ExtTcpPort},${ExtHttpPort}

    C:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe install EventStore C:\EventStore\v3.0.1\EventStore.ClusterNode.exe --config C:\EventStore\EventStore-Config.yaml
    C:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe install EventStore C:\EventStore\v3.0.2\EventStore.ClusterNode.exe --config C:\EventStore\EventStore-Config.yaml
    C:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe set EventStore Description "The EventStore service."


  3. @pbolduc pbolduc revised this gist Mar 9, 2015. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions EventStoreScriptExtensionProvisionFile.ps1
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,3 @@

    param (
    [Int]
    $clusterSize,
    @@ -132,9 +131,9 @@ Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.1.zip" -destination "C:\EventS

    New-EventStoreConfigFile

    #choco install logstash
    choco install nssm --version 2.24.0
    choco install timberwinr
    #choco install logstash
    #choco install timberwinr

    #
    #
  4. @pbolduc pbolduc revised this gist Mar 9, 2015. 2 changed files with 66 additions and 33 deletions.
    61 changes: 41 additions & 20 deletions EventStoreScriptExtensionProvisionFile.ps1
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,19 @@ param (
    [string]
    $VMName,
    [Int]
    $nodeNumber
    $nodeNumber,
    [Int]
    $IntIp,
    [Int]
    $ExtIp,
    [Int]
    $IntTcpPort = 1112,
    [Int]
    $IntHttpPort = 2112,
    [Int]
    $ExtTcpPort = 1113,
    [Int]
    $ExtHttpPort = 2113
    )


    @@ -33,7 +45,7 @@ function Download-EventStore($DownloadUrl, $SaveToPath) {
    }

    function Format-DataDisks() {
    $Interleave = 65536
    $Interleave = 65536 # is this the best value for EventStore?
    $uninitializedDisks = Get-PhysicalDisk -CanPool $true

    $poolDisks = $uninitializedDisks
    @@ -79,55 +91,61 @@ function New-EventStoreConfigFile() {
    } while ($ip -eq $null)

    #
    $seeds += "'" + $ip + ":2112" + "'"
    $seeds += "'" + $ip + ":" + $IntHttpPort + "'"
    }
    }

    $gossipSeed = $seeds -join ','


    # this is a bit of a hack that depends on the BGInfo plugin. Is there a better way to determine this?
    #$publicIp = PS C:\Users\eventstore> (Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Azure\BGInfo").PublicIp

    $configFile = "C:\EventStore\EventStore-Config.yaml"
    $ipAddress = (Resolve-DnsName $env:COMPUTERNAME -Type A).IPAddress
    $configFile = "D:\EventStore\EventStore-Config.yaml"

    $IntIp = $ipAddress
    $ExtIp = $ipAddress

    "# EventStore configuration file. Created at: $(Get-Date -Format 'u')" | Out-File -FilePath $configFile
    "Db: F:\Data\eventstore" | Out-File -FilePath $configFile -Append
    "Log: D:\Logs\eventstore" | Out-File -FilePath $configFile -Append
    "IntIp: $ipAddress" | Out-File -FilePath $configFile -Append
    "ExtIp: $ipAddress" | Out-File -FilePath $configFile -Append
    "IntTcpPort: 1112" | Out-File -FilePath $configFile -Append
    "IntHttpPort: 2112" | Out-File -FilePath $configFile -Append
    "ExtTcpPort: 1113" | Out-File -FilePath $configFile -Append
    "ExtHttpPort: 2113" | Out-File -FilePath $configFile -Append
    "IntIp: $IntIp" | Out-File -FilePath $configFile -Append
    "ExtIp: $ExtIp" | Out-File -FilePath $configFile -Append
    "IntTcpPort: $IntTcpPort" | Out-File -FilePath $configFile -Append
    "IntHttpPort: $IntHttpPort" | Out-File -FilePath $configFile -Append
    "ExtTcpPort: $ExtTcpPort" | Out-File -FilePath $configFile -Append
    "ExtHttpPort: $ExtHttpPort" | Out-File -FilePath $configFile -Append
    "DiscoverViaDns: false" | Out-File -FilePath $configFile -Append
    "GossipSeed: [$gossipSeed]" | Out-File -FilePath $configFile -Append
    "ClusterSize: $clusterSize" | Out-File -FilePath $configFile -Append
    }

    #
    # Azure VM's have Temporary Storage on D:\
    # Azure VM's have Temporary Storage on D:\ - Store only log data / temp files there
    #

    Format-DataDisks

    Install-Chocolatey -InstallToPath "D:\Chocolatey"
    Install-Chocolatey -InstallToPath "C:\Chocolatey"
    Download-EventStore -DownloadUrl "http://download.geteventstore.com/binaries/EventStore-OSS-Win-v3.0.1.zip" -SaveToPath "D:\EventStore-OSS-Win-v3.0.1.zip"
    Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.1.zip" -destination "D:\EventStore\v3.0.1\"
    Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.1.zip" -destination "C:\EventStore\v3.0.1\"

    New-EventStoreConfigFile

    #choco install logstash
    choco install nssm --version 2.24.0
    choco install timberwinr

    #
    #
    #
    $ipAddress = (Resolve-DnsName $env:COMPUTERNAME -Type A).IPAddress

    #Database Node Internal HTTP Interface (open source and commercial)
    netsh http add urlacl url=http://$ipAddress:2112/ user="NT AUTHORITY\LOCAL SERVICE"
    netsh http add urlacl url=http://${ipAddress}:${IntHttpPort}/ user="NT AUTHORITY\LOCAL SERVICE"

    # Database Node External HTTP Interface (open source and commercial)
    netsh http add urlacl url=http://$ipAddress:2113/ user="NT AUTHORITY\LOCAL SERVICE"
    netsh http add urlacl url=http://${ipAddress}:${ExtHttpPort}/ user="NT AUTHORITY\LOCAL SERVICE"

    # Manager Node Internal HTTP Interface (commercial only)
    #netsh http add urlacl url=http://$ipAddress:30777/ user="NT AUTHORITY\LOCAL SERVICE"
    @@ -136,8 +154,11 @@ netsh http add urlacl url=http://$ipAddress:2113/ user="NT AUTHORITY\LOCAL SERVI
    #netsh http add urlacl url=http://$ipAddress:30778/ user="NT AUTHORITY\LOCAL SERVICE"

    # Added all the ports, but think I only require the 2112,2113 ports
    New-NetFirewallRule -Name Allow_EventStore_In -DisplayName "Allow inbound Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort 1112,1113,2112,2113
    New-NetFirewallRule -Name Allow_EventStore_Int_In -DisplayName "Allow inbound Internal Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort ${IntTcpPort},${IntHttpPort}
    New-NetFirewallRule -Name Allow_EventStore_Ext_In -DisplayName "Allow inbound External Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort ${ExtTcpPort},${ExtHttpPort}

    C:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe install EventStore C:\EventStore\v3.0.1\EventStore.ClusterNode.exe --config C:\EventStore\EventStore-Config.yaml
    C:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe set EventStore Description "The EventStore service."


    D:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe install EventStore d:\EventStore\v3.0.1\EventStore.ClusterNode.exe --config D:\EventStore\EventStore-Config.yaml
    D:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe set EventStore Description "The EventStore service."
    net start EventStore
    #net start EventStore
    38 changes: 25 additions & 13 deletions ProvisionEventStore.ps1
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,9 @@
    <#
    .SYNOPSIS
    .DESCRIPTION
    .EXAMPLE
    #>

    # http://azure.microsoft.com/en-us/documentation/articles/install-configure-powershell/
    # Add-AzureAccount

    @@ -14,7 +20,8 @@ param
    [String]$ImageName,
    [String]$AffinityGroup,
    [String]$TargetStorageAccountName,
    [Int]$MaxDiskPerStorageAccount = 40,
    [Int]$MaxDisksPerStorageAccount = 40,
    [Int]$MaxDataDisksPerVirtualMachine = 0,
    [String]$AvailabilitySetName,
    [String]$VNetName,
    [String]$VNetSubnetName,
    @@ -100,9 +107,14 @@ function Create-EventStoreNodes {

    $vms = @()

    $numberOfDisks = (Get-AzureRoleSize -InstanceSize $InstanceSize).MaxDataDiskCount

    $totalDiskCount = $ClusterSize * ($numberOfDisks+1) # OS Disk + N data disks $MaxDiskPerStorageAccount
    $numberOfDataDisks = (Get-AzureRoleSize -InstanceSize $InstanceSize).MaxDataDiskCount

    # if we are limiting the total data disks
    if ($MaxDataDisksPerVirtualMachine -ge 1) {
    $numberOfDataDisks = [System.Math]::Min($numberOfDataDisks,$MaxDataDisksPerVirtualMachine)
    }

    $totalDiskCount = $ClusterSize * ($numberOfDataDisks+1) # OS Disk + N data disks $MaxDiskPerStorageAccount

    if ($totalDiskCount -ge 40) {
    Write-Warning "You may have too many disks per storage account. Your performance may degrade. See http://blogs.msdn.com/b/mast/archive/2014/10/14/configuring-azure-virtual-machines-for-optimal-storage-performance.aspx"
    @@ -122,15 +134,15 @@ function Create-EventStoreNodes {
    }

    if ($true) {
    $LocalTcpPort = 1113
    $LocalHttpPort = 2113
    $StandardTcpPort = 1113
    $StandardHttpPort = 2113

    $PublicTcpPort = $LocalTcpPort + ($node - 1) * 100
    $PublicHttpPort = $LocalHttpPort + ($node - 1) * 100
    $ExtTcpPort = $StandardTcpPort + ($node - 1) * 100
    $ExtHttpPort = $StandardHttpPort + ($node - 1) * 100

    # http://michaelwasham.com/windows-azure-powershell-reference-guide/configuring-disks-endpoints-vms-powershell/
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreTcp' -LocalPort $LocalTcpPort -PublicPort $PublicTcpPort -Protocol Tcp
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreHttp' -LocalPort $LocalHttpPort -PublicPort $PublicHttpPort -Protocol Tcp
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreTcp' -LocalPort $ExtTcpPort -PublicPort $ExtTcpPort -Protocol Tcp
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreHttp' -LocalPort $ExtHttpPort -PublicPort $ExtHttpPort -Protocol Tcp
    }

    $vm = $vm | Set-AzureVMCustomScriptExtension `
    @@ -139,15 +151,15 @@ function Create-EventStoreNodes {
    -ContainerName $CustomScriptExtensionContainerName `
    -FileName $CustomScriptExtensionProvisionFile `
    -Run $CustomScriptExtensionProvisionFile `
    -Argument "-clusterSize $ClusterSize -VMName $VMName -nodeNumber $node"
    -Argument "-clusterSize $ClusterSize -VMName $VMName -nodeNumber $node -ExtTcpPort $ExtTcpPort -ExtHttpPort $ExtHttpPort"

    # Attach the maximum data disks allowed for the virtual machine size
    if ($DataDiskSize -gt 0) {

    $minSizeInGB = 4 # min size for each disk to be stripped: All disks must be at least 4 GB.
    $sizeInGB = [int][System.Math]::Max([System.Math]::Ceiling($DataDiskSize / $numberOfDisks), $minSizeInGB)
    $sizeInGB = [int][System.Math]::Max([System.Math]::Ceiling($DataDiskSize / $numberOfDataDisks), $minSizeInGB)

    for ($index = 0; $index -lt $numberOfDisks; $index++) {
    for ($index = 0; $index -lt $numberOfDataDisks; $index++) {
    $label = "Data disk " + $index
    # The maximum number of data disks that may simultaneously use read caching is 4.
    $vm = $vm | Add-AzureDataDisk -CreateNew -DiskSizeInGB $sizeInGB -DiskLabel $label -LUN $index -HostCaching None
  5. @pbolduc pbolduc created this gist Mar 5, 2015.
    143 changes: 143 additions & 0 deletions EventStoreScriptExtensionProvisionFile.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,143 @@

    param (
    [Int]
    $clusterSize,
    [string]
    $VMName,
    [Int]
    $nodeNumber
    )


    function Extract-ZipFile($file, $destination)
    {
    if (![System.IO.Directory]::Exists($destination)) {
    [System.IO.Directory]::CreateDirectory($destination)
    }
    $shell = new-object -com shell.application
    $zip = $shell.NameSpace($file)

    foreach($item in $zip.items()) {
    $shell.Namespace($destination).copyhere($item)
    }
    }

    function Install-Chocolatey($InstallToPath) {
    $env:ChocolateyInstall = $InstallToPath
    iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
    }

    function Download-EventStore($DownloadUrl, $SaveToPath) {
    $client = new-object System.Net.WebClient
    $client.DownloadFile($DownloadUrl, $SaveToPath)
    }

    function Format-DataDisks() {
    $Interleave = 65536
    $uninitializedDisks = Get-PhysicalDisk -CanPool $true

    $poolDisks = $uninitializedDisks
    $numberOfDisksPerPool = $poolDisks.Length

    $poolName = "Data Storage Pool"
    $newPool = New-StoragePool -FriendlyName $poolName -StorageSubSystemFriendlyName "Storage Spaces*" -PhysicalDisks $poolDisks

    $virtualDiskJob = New-VirtualDisk -StoragePoolFriendlyName $poolName -FriendlyName $poolName -ResiliencySettingName Simple -ProvisioningType Fixed -Interleave $Interleave `
    -NumberOfDataCopies 1 -NumberOfColumns $numberOfDisksPerPool -UseMaximumSize -AsJob

    Receive-Job -Job $virtualDiskJobs -Wait
    Wait-Job -Job $virtualDiskJobs
    Remove-Job -Job $virtualDiskJobs

    # Initialize and format the virtual disks on the pools
    $formatted = Get-VirtualDisk | Initialize-Disk -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem NTFS -Confirm:$false

    # Create the data directory
    $formatted | ForEach-Object {
    # Get current drive letter.
    $downloadDriveLetter = $_.DriveLetter

    # Create the data directory
    $dataDirectory = "$($downloadDriveLetter):\Data"

    New-Item $dataDirectory -Type directory -Force | Out-Null
    }

    # Dive time to the storage service to pick up the changes
    Start-Sleep -Seconds 60
    }

    function New-EventStoreConfigFile() {

    $seeds = @()
    for ($n = 1; $n -le $clusterSize; $n++) {
    $nodeName = $VMName + "-" + $n
    if ($nodeName -ne $env:COMPUTERNAME) {
    do {
    # the other nodes may not be running yet, wait for them to return an ip address
    $ip = (Resolve-DnsName $nodeName -Type A -ErrorAction SilentlyContinue).IPAddress
    } while ($ip -eq $null)

    #
    $seeds += "'" + $ip + ":2112" + "'"
    }
    }

    $gossipSeed = $seeds -join ','


    $ipAddress = (Resolve-DnsName $env:COMPUTERNAME -Type A).IPAddress
    $configFile = "D:\EventStore\EventStore-Config.yaml"

    "# EventStore configuration file. Created at: $(Get-Date -Format 'u')" | Out-File -FilePath $configFile
    "Db: F:\Data\eventstore" | Out-File -FilePath $configFile -Append
    "Log: D:\Logs\eventstore" | Out-File -FilePath $configFile -Append
    "IntIp: $ipAddress" | Out-File -FilePath $configFile -Append
    "ExtIp: $ipAddress" | Out-File -FilePath $configFile -Append
    "IntTcpPort: 1112" | Out-File -FilePath $configFile -Append
    "IntHttpPort: 2112" | Out-File -FilePath $configFile -Append
    "ExtTcpPort: 1113" | Out-File -FilePath $configFile -Append
    "ExtHttpPort: 2113" | Out-File -FilePath $configFile -Append
    "DiscoverViaDns: false" | Out-File -FilePath $configFile -Append
    "GossipSeed: [$gossipSeed]" | Out-File -FilePath $configFile -Append
    "ClusterSize: $clusterSize" | Out-File -FilePath $configFile -Append
    }

    #
    # Azure VM's have Temporary Storage on D:\
    #

    Format-DataDisks

    Install-Chocolatey -InstallToPath "D:\Chocolatey"
    Download-EventStore -DownloadUrl "http://download.geteventstore.com/binaries/EventStore-OSS-Win-v3.0.1.zip" -SaveToPath "D:\EventStore-OSS-Win-v3.0.1.zip"
    Extract-ZipFile -file "D:\EventStore-OSS-Win-v3.0.1.zip" -destination "D:\EventStore\v3.0.1\"

    New-EventStoreConfigFile

    #choco install logstash
    choco install nssm --version 2.24.0

    #
    #
    #
    $ipAddress = (Resolve-DnsName $env:COMPUTERNAME -Type A).IPAddress

    #Database Node Internal HTTP Interface (open source and commercial)
    netsh http add urlacl url=http://$ipAddress:2112/ user="NT AUTHORITY\LOCAL SERVICE"

    # Database Node External HTTP Interface (open source and commercial)
    netsh http add urlacl url=http://$ipAddress:2113/ user="NT AUTHORITY\LOCAL SERVICE"

    # Manager Node Internal HTTP Interface (commercial only)
    #netsh http add urlacl url=http://$ipAddress:30777/ user="NT AUTHORITY\LOCAL SERVICE"

    # Manager Node External HTTP Interface (commercial only)
    #netsh http add urlacl url=http://$ipAddress:30778/ user="NT AUTHORITY\LOCAL SERVICE"

    # Added all the ports, but think I only require the 2112,2113 ports
    New-NetFirewallRule -Name Allow_EventStore_In -DisplayName "Allow inbound Event Store traffic" -Protocol TCP -Direction Inbound -Action Allow -LocalPort 1112,1113,2112,2113

    D:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe install EventStore d:\EventStore\v3.0.1\EventStore.ClusterNode.exe --config D:\EventStore\EventStore-Config.yaml
    D:\Chocolatey\lib\NSSM.2.24.0\Tools\nssm-2.24\win64\nssm.exe set EventStore Description "The EventStore service."
    net start EventStore
    172 changes: 172 additions & 0 deletions ProvisionEventStore.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,172 @@
    # http://azure.microsoft.com/en-us/documentation/articles/install-configure-powershell/
    # Add-AzureAccount

    param
    (
    [Int]$ClusterSize,
    [Int]$DataDiskSize = 0,
    [String]$Location,
    [String]$InstanceSize,
    [String]$username,
    [String]$password,
    [String]$ServiceName,
    [String]$VMName,
    [String]$ImageName,
    [String]$AffinityGroup,
    [String]$TargetStorageAccountName,
    [Int]$MaxDiskPerStorageAccount = 40,
    [String]$AvailabilitySetName,
    [String]$VNetName,
    [String]$VNetSubnetName,
    [String]$CustomScriptExtensionStorageAccountName,
    [String]$CustomScriptExtensionStorageAccountKey,
    [String]$CustomScriptExtensionContainerName,
    [String]$CustomScriptExtensionProvisionFile
    )

    function Get-RandomStorageAccountName {
    param (
    [Parameter(Mandatory = $true)]
    [String]
    $prefix,
    [ValidateRange(3,24)]
    [Int]
    $maxLength = 24
    )

    $name = $prefix.ToLower()
    while ($name.Length -lt 24) {
    $name = $name + [char](Get-Random -Minimum 97 -Maximum 122)
    }
    return $name
    }

    function Ensure-AzureAffinityGroup {
    param
    (
    [Parameter(Mandatory = $true)]
    [String]
    $name,
    [Parameter(Mandatory = $true)]
    [string]$location
    )

    $affinityGroup = Get-AzureAffinityGroup -Name $name -ErrorAction SilentlyContinue
    if ($affinityGroup -eq $null) {
    New-AzureAffinityGroup -Location $location -Name $name | Out-Null
    } elseif ($affinityGroup.Location -ne $location) {
    $actualLocation = $affinityGroup.Location
    Write-Error "Affinity Group '$name' exists, but is not in location '$location'. It is in location '$actualLocation'."
    }
    }

    function Ensure-StorageAccount() {
    if ((Get-AzureStorageAccount -StorageAccountName $TargetStorageAccountName -ErrorAction SilentlyContinue) -eq $null) {

    $storageAccountName = Get-RandomStorageAccountName($TargetStorageAccountName)
    New-AzureStorageAccount -AffinityGroup $AffinityGroup -StorageAccountName $storageAccountName -Type Standard_LRS | Out-Null

    # Wait for the storage account to be available so we can use it
    Write-Verbose "$(Get-Date -Format 'T') Waiting for storage account $storageAccountName to be available..."
    while ((Get-AzureStorageAccount -StorageAccountName $storageAccountName).StatusOfPrimary -ne "Available") {
    Start-Sleep -Seconds 1
    }
    return $storageAccountName
    } else {
    return $TargetStorageAccountName
    }
    }

    function Create-EventStoreNodes {
    param (
    [Parameter(Mandatory = $true)]
    [String]
    $StorageAccountName
    )
    <# Subscription Limits - http://azure.microsoft.com/en-us/documentation/articles/azure-subscription-service-limits/
    Default Max limit
    Cores 20 10,000
    Storage accounts per subscription - 100 100
    Max 8 KB IOPS per persistent disk (Basic Tier) 300
    Max 8 KB IOPS per persistent disk (Standard Tier) 500
    Total Request Rate (assuming 1KB object size) per storage account 20,000
    Target Throughput for Single Blob Up to 60 MB per second, or up to 500 requests per second
    #>

    # create the cloud service first so that it will be ready when the VM are created below
    New-AzureService -ServiceName $ServiceName -AffinityGroup $AffinityGroup | Out-Null

    $vms = @()

    $numberOfDisks = (Get-AzureRoleSize -InstanceSize $InstanceSize).MaxDataDiskCount

    $totalDiskCount = $ClusterSize * ($numberOfDisks+1) # OS Disk + N data disks $MaxDiskPerStorageAccount

    if ($totalDiskCount -ge 40) {
    Write-Warning "You may have too many disks per storage account. Your performance may degrade. See http://blogs.msdn.com/b/mast/archive/2014/10/14/configuring-azure-virtual-machines-for-optimal-storage-performance.aspx"
    }

    for ($node=1;$node -le $ClusterSize;$node++) {
    $name = $VMName + "-" + $node

    $vm = New-AzureVMConfig -ImageName $ImageName -InstanceSize $InstanceSize -Name $name -AvailabilitySetName $AvailabilitySetName
    $vm = $vm | Set-AzureSubnet -SubnetNames $VNetSubnetName

    $isWindows = $true
    if ($isWindows) {
    $vm = $vm | Add-AzureProvisioningConfig -AdminUsername $username -Password $password -Windows
    } else {
    $vm = $vm | Add-AzureProvisioningConfig -LinuxUser $username -Password $password -Linux
    }

    if ($true) {
    $LocalTcpPort = 1113
    $LocalHttpPort = 2113

    $PublicTcpPort = $LocalTcpPort + ($node - 1) * 100
    $PublicHttpPort = $LocalHttpPort + ($node - 1) * 100

    # http://michaelwasham.com/windows-azure-powershell-reference-guide/configuring-disks-endpoints-vms-powershell/
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreTcp' -LocalPort $LocalTcpPort -PublicPort $PublicTcpPort -Protocol Tcp
    $vm = $vm | Add-AzureEndpoint -Name 'EventStoreHttp' -LocalPort $LocalHttpPort -PublicPort $PublicHttpPort -Protocol Tcp
    }

    $vm = $vm | Set-AzureVMCustomScriptExtension `
    -StorageAccountName $CustomScriptExtensionStorageAccountName `
    -StorageAccountKey $CustomScriptExtensionStorageAccountKey `
    -ContainerName $CustomScriptExtensionContainerName `
    -FileName $CustomScriptExtensionProvisionFile `
    -Run $CustomScriptExtensionProvisionFile `
    -Argument "-clusterSize $ClusterSize -VMName $VMName -nodeNumber $node"

    # Attach the maximum data disks allowed for the virtual machine size
    if ($DataDiskSize -gt 0) {

    $minSizeInGB = 4 # min size for each disk to be stripped: All disks must be at least 4 GB.
    $sizeInGB = [int][System.Math]::Max([System.Math]::Ceiling($DataDiskSize / $numberOfDisks), $minSizeInGB)

    for ($index = 0; $index -lt $numberOfDisks; $index++) {
    $label = "Data disk " + $index
    # The maximum number of data disks that may simultaneously use read caching is 4.
    $vm = $vm | Add-AzureDataDisk -CreateNew -DiskSizeInGB $sizeInGB -DiskLabel $label -LUN $index -HostCaching None
    }
    }

    $vms += $vm
    }

    # create all of the VMs
    New-AzureVM -ServiceName $ServiceName -VNetName $VNetName -VMs $vms | Out-Null
    }

    Write-Verbose "$(Get-Date -Format 'T') Ensuring Affinity Group '$AffinityGroup' exists and is in '$Location' location."
    Ensure-AzureAffinityGroup $AffinityGroup $Location
    $storageAccountName = Ensure-StorageAccount

    $SubscriptionName = (Get-AzureSubscription).SubscriptionName
    Set-AzureSubscription -SubscriptionName $SubscriptionName -CurrentStorageAccount $storageAccountName

    Write-Verbose "$(Get-Date -Format 'T') Creating Virtual Machines"
    Create-EventStoreNodes -StorageAccountName $storageAccountName