Last active
December 10, 2015 20:38
-
-
Save abombss/4489558 to your computer and use it in GitHub Desktop.
Automatically Install AppFabric Cache Cluster and Nodes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Uninstall-AppFabricCacheNode { | |
#TODO: Should try and remove the configuration stuff | |
gwmi win32_product -filter "Name LIKE 'AppFabric%'" |% { | |
$key="IdentifyingNumber=`"$($_.IdentifyingNumber)`",Name=`"$($_.Name)`",version=`"$($_.Version)`""; | |
([wmi]"Win32_Product.$key").uninstall() | |
} | |
} | |
function Reset-AppFabricCacheDatabase { | |
param( | |
[Parameter(Mandatory=$true)] | |
[string]$ConnectionString | |
) | |
$con = new-object System.Data.SqlClient.SqlConnection | |
try { | |
$con.ConnectionString = $ConnectionString | |
Write-Verbose "Connection to $ConnectionString" | |
$con.Open() | |
$cmd = $con.CreateCommand() | |
$cmd.CommandText="SELECT DB_NAME() AS [Database]" | |
$dbName = $cmd.ExecuteScalar() | |
$cmd.Dispose() | |
Write-Verbose "Found database $dbName" | |
$cmd = $con.CreateCommand() | |
$cmd.CommandText = @" | |
USE master; | |
ALTER DATABASE [$dbName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; | |
DROP DATABASE [$dbName]; | |
"@ | |
Write-Verbose "Dropping database $dbName" | |
$cmd.ExecuteNonQuery() | |
$cmd.Dispose() | |
$cmd = $con.CreateCommand() | |
$cmd.CommandText = "CREATE DATABASE [$dbName]; ALTER DATABASE [$dbName] SET MULTI_USER" | |
Write-Verbose "Creating database $dbName" | |
$cmd.ExecuteNonQuery() | |
$cmd.Dispose() | |
$cmd = $null | |
} catch { | |
Write-Error $error[0] | |
} finally { | |
if ($cmd) { $cmd.Dispose() } | |
$con.Close() | |
} | |
} | |
function Add-AccountPrivilege { | |
param([string]$Account, [string]$Privilege="SeServiceLogonRight") | |
try { | |
[MyLsaWrapper.LsaWrapperCaller] | |
} catch { | |
# Special Powershell Command to Grant the service account LogOn as Service Rights | |
Add-Type @' | |
using System; | |
using System.Collections.Generic; | |
using System.Text; | |
namespace MyLsaWrapper | |
{ | |
using System.Runtime.InteropServices; | |
using System.Security; | |
using System.Management; | |
using System.Runtime.CompilerServices; | |
using System.ComponentModel; | |
using LSA_HANDLE = IntPtr; | |
[StructLayout(LayoutKind.Sequential)] | |
struct LSA_OBJECT_ATTRIBUTES | |
{ | |
internal int Length; | |
internal IntPtr RootDirectory; | |
internal IntPtr ObjectName; | |
internal int Attributes; | |
internal IntPtr SecurityDescriptor; | |
internal IntPtr SecurityQualityOfService; | |
} | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
struct LSA_UNICODE_STRING | |
{ | |
internal ushort Length; | |
internal ushort MaximumLength; | |
[MarshalAs(UnmanagedType.LPWStr)] | |
internal string Buffer; | |
} | |
sealed class Win32Sec | |
{ | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), | |
SuppressUnmanagedCodeSecurityAttribute] | |
internal static extern uint LsaOpenPolicy( | |
LSA_UNICODE_STRING[] SystemName, | |
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, | |
int AccessMask, | |
out IntPtr PolicyHandle | |
); | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), | |
SuppressUnmanagedCodeSecurityAttribute] | |
internal static extern uint LsaAddAccountRights( | |
LSA_HANDLE PolicyHandle, | |
IntPtr pSID, | |
LSA_UNICODE_STRING[] UserRights, | |
int CountOfRights | |
); | |
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), | |
SuppressUnmanagedCodeSecurityAttribute] | |
internal static extern int LsaLookupNames2( | |
LSA_HANDLE PolicyHandle, | |
uint Flags, | |
uint Count, | |
LSA_UNICODE_STRING[] Names, | |
ref IntPtr ReferencedDomains, | |
ref IntPtr Sids | |
); | |
[DllImport("advapi32")] | |
internal static extern int LsaNtStatusToWinError(int NTSTATUS); | |
[DllImport("advapi32")] | |
internal static extern int LsaClose(IntPtr PolicyHandle); | |
[DllImport("advapi32")] | |
internal static extern int LsaFreeMemory(IntPtr Buffer); | |
} | |
/// <summary> | |
/// This class is used to grant "Log on as a service", "Log on as a batchjob", "Log on localy" etc. | |
/// to a user. | |
/// </summary> | |
public sealed class LsaWrapper : IDisposable | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
struct LSA_TRUST_INFORMATION | |
{ | |
internal LSA_UNICODE_STRING Name; | |
internal IntPtr Sid; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct LSA_TRANSLATED_SID2 | |
{ | |
internal SidNameUse Use; | |
internal IntPtr Sid; | |
internal int DomainIndex; | |
uint Flags; | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
struct LSA_REFERENCED_DOMAIN_LIST | |
{ | |
internal uint Entries; | |
internal LSA_TRUST_INFORMATION Domains; | |
} | |
enum SidNameUse : int | |
{ | |
User = 1, | |
Group = 2, | |
Domain = 3, | |
Alias = 4, | |
KnownGroup = 5, | |
DeletedAccount = 6, | |
Invalid = 7, | |
Unknown = 8, | |
Computer = 9 | |
} | |
enum Access : int | |
{ | |
POLICY_READ = 0x20006, | |
POLICY_ALL_ACCESS = 0x00F0FFF, | |
POLICY_EXECUTE = 0X20801, | |
POLICY_WRITE = 0X207F8 | |
} | |
const uint STATUS_ACCESS_DENIED = 0xc0000022; | |
const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; | |
const uint STATUS_NO_MEMORY = 0xc0000017; | |
IntPtr lsaHandle; | |
public LsaWrapper() | |
: this(null) | |
{ } | |
// // local system if systemName is null | |
public LsaWrapper(string systemName) | |
{ | |
LSA_OBJECT_ATTRIBUTES lsaAttr; | |
lsaAttr.RootDirectory = IntPtr.Zero; | |
lsaAttr.ObjectName = IntPtr.Zero; | |
lsaAttr.Attributes = 0; | |
lsaAttr.SecurityDescriptor = IntPtr.Zero; | |
lsaAttr.SecurityQualityOfService = IntPtr.Zero; | |
lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES)); | |
lsaHandle = IntPtr.Zero; | |
LSA_UNICODE_STRING[] system = null; | |
if (systemName != null) | |
{ | |
system = new LSA_UNICODE_STRING[1]; | |
system[0] = InitLsaString(systemName); | |
} | |
uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, | |
(int)Access.POLICY_ALL_ACCESS, out lsaHandle); | |
if (ret == 0) | |
return; | |
if (ret == STATUS_ACCESS_DENIED) | |
{ | |
throw new UnauthorizedAccessException(); | |
} | |
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) | |
{ | |
throw new OutOfMemoryException(); | |
} | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); | |
} | |
public void AddPrivileges(string account, string privilege) | |
{ | |
IntPtr pSid = GetSIDInformation(account); | |
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; | |
privileges[0] = InitLsaString(privilege); | |
uint ret = Win32Sec.LsaAddAccountRights(lsaHandle, pSid, privileges, 1); | |
if (ret == 0) | |
return; | |
if (ret == STATUS_ACCESS_DENIED) | |
{ | |
throw new UnauthorizedAccessException(); | |
} | |
if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) | |
{ | |
throw new OutOfMemoryException(); | |
} | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); | |
} | |
public void Dispose() | |
{ | |
if (lsaHandle != IntPtr.Zero) | |
{ | |
Win32Sec.LsaClose(lsaHandle); | |
lsaHandle = IntPtr.Zero; | |
} | |
GC.SuppressFinalize(this); | |
} | |
~LsaWrapper() | |
{ | |
Dispose(); | |
} | |
// helper functions | |
IntPtr GetSIDInformation(string account) | |
{ | |
LSA_UNICODE_STRING[] names = new LSA_UNICODE_STRING[1]; | |
LSA_TRANSLATED_SID2 lts; | |
IntPtr tsids = IntPtr.Zero; | |
IntPtr tdom = IntPtr.Zero; | |
names[0] = InitLsaString(account); | |
lts.Sid = IntPtr.Zero; | |
Console.WriteLine("String account: {0}", names[0].Length); | |
int ret = Win32Sec.LsaLookupNames2(lsaHandle, 0, 1, names, ref tdom, ref tsids); | |
if (ret != 0) | |
throw new Win32Exception(Win32Sec.LsaNtStatusToWinError(ret)); | |
lts = (LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids, | |
typeof(LSA_TRANSLATED_SID2)); | |
Win32Sec.LsaFreeMemory(tsids); | |
Win32Sec.LsaFreeMemory(tdom); | |
return lts.Sid; | |
} | |
static LSA_UNICODE_STRING InitLsaString(string s) | |
{ | |
// Unicode strings max. 32KB | |
if (s.Length > 0x7ffe) | |
throw new ArgumentException("String too long"); | |
LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING(); | |
lus.Buffer = s; | |
lus.Length = (ushort)(s.Length * sizeof(char)); | |
lus.MaximumLength = (ushort)(lus.Length + sizeof(char)); | |
return lus; | |
} | |
} | |
public class LsaWrapperCaller | |
{ | |
public static void AddPrivileges(string account, string privilege) | |
{ | |
using (LsaWrapper lsaWrapper = new LsaWrapper()) | |
{ | |
lsaWrapper.AddPrivileges(account, privilege); | |
} | |
} | |
} | |
} | |
'@ | |
#Swallow the exception, because it should be missing type and we just added it | |
} | |
# Run the command to grant the account login as service rights | |
[MyLsaWrapper.LsaWrapperCaller]::AddPrivileges($Account, $Privilege) | |
} | |
function Install-AppFabricCacheNode { | |
param( | |
[string]$SetupFile=$null, | |
[string]$LogFile=".\appfabricsetup.txt", | |
[string]$WorkingDirectory=".", | |
[string]$Provider=$null, | |
[string]$ConnectionString=$null, | |
[string]$ServiceAccount, | |
[string]$Password, | |
[int]$CachePort=22233, | |
[int]$ClusterPort=22234, | |
[int]$ArbitrationPort=22235, | |
[int]$ReplicationPort=22236, | |
[switch]$NewCacheCluster=$false | |
); | |
$CurrentHost = ([System.Net.Dns]::GetHostByName(($env:computerName))).HostName | |
$AbsoluteWorkingDir = Resolve-Path $WorkingDirectory | |
if (![System.IO.Path]::IsPathRooted($SetupFile)) { | |
$SetupFile = Resolve-Path $SetupFile | |
} | |
if (![System.IO.Path]::IsPathRooted($LogFile)) { | |
$LogFile = Join-Path (Resolve-Path (Split-Path $LogFile -Parent)) (Split-Path $LogFile -Leaf) | |
} | |
$installArgs = @( | |
"/install" | |
"cachingservice,cacheclient,cacheadmin" | |
"/l:`"$LogFile`"" | |
) | |
$installParameters = @{ | |
FilePath=$SetupFile | |
ArgumentList=$installArgs | |
Wait=$true | |
WorkingDirectory=$AbsoluteWorkingDir | |
Verb='runas' | |
} | |
Write-Verbose "Silently starting appfabric installation" | |
Start-Process @installParameters | |
Write-Verbose "Changing credentials of AppFabricCachingService to service account credentials" | |
& (Join-Path (Join-Path $Env:WinDir system32) sc.exe) @( | |
'config' | |
'AppFabricCachingService' | |
'obj=' | |
"`"$ServiceAccount`"" | |
'password=' | |
"`"$Password`"" | |
) | |
Write-Verbose "Adding Log On as Service right for Service Account" | |
try { | |
Add-AccountPrivilege -Account $ServiceAccount -Privilege "SeServiceLogonRight" | |
} catch { | |
Add-AccountPrivilege -Account $ServiceAccount -Privilege "SeServiceLogonRight" | |
} | |
try { | |
Add-AccountPrivilege -Account $ServiceAccount -Privilege "SeBatchLogonRight" | |
} catch{ | |
Add-AccountPrivilege -Account $ServiceAccount -Privilege "SeBatchLogonRight" | |
} | |
Write-Verbose "Adding Scheduled Task to Start-CacheHost at machine boot" | |
# In 2012 this becomes easier because there is a cmdlet for schedule task administration | |
$scheduledTaskArgs = @( | |
'/Create', | |
'/TN',"AppFabricCache-StartCacheHost" | |
'/SC','ONSTART', | |
'/RL','Highest', | |
'/RU',"`"$ServiceAccount`"", | |
'/RP',"`"$Password`"", | |
'/DELAY',"0000:30" | |
'/F', | |
'/TR',"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy RemoteSigned -NoProfile -NoLogo -Command {import-module DistributedCacheAdministration; use-cachecluster; start-cachehost -hostname $CurrentHost -cacheport $CachePort;}" | |
) | |
& (Join-Path (Join-Path $Env:WinDir system32) schtasks.exe) $scheduledTaskArgs | |
Write-Verbose "Opening Firewall for caching ports" | |
# Open the firewall ports, run on each node in the cluster | |
# OK if all of these don't run because some may already be open, look into a better way to verify the ports are open | |
netsh advfirewall firewall set rule group="Appfabric Server: AppFabric Caching Service" new enable=Yes | |
netsh advfirewall firewall set rule name="Remote Service Management (RPC)" profile=domain new enable=Yes | |
netsh advfirewall firewall set rule name="Remote Service Management (RPC-EPMAP)" profile=domain new enable=Yes | |
netsh advfirewall firewall set rule name="Remote Service Management (NP-In)" profile=domain new enable=Yes | |
# Hack, manually add the appfab powershell module path, | |
# look into registery settings or something better to get the location of the module path | |
# or see if wmi will work to get the new PSModulePath value and then we can splice it into the current PSModulePath | |
# | |
$Env:PSModulePath += ";c:\Program Files\AppFabric 1.1 for Windows Server\PowershellModules" | |
Import-Module DistributedCacheAdministration | |
Import-Module DistributedCacheConfiguration | |
if ($NewCacheCluster) { | |
Write-Verbose "Creating a new cache cluster" | |
# TODO: Parameterize this command better | |
New-CacheCluster -Provider $Provider -ConnectionString $ConnectionString -Size Small | |
} | |
Use-CacheCluster -Provider $Provider -ConnectionString $ConnectionString | |
Write-Verbose "Registering cache host" | |
Register-CacheHost -HostName $CurrentHost -Provider $Provider -ConnectionString $ConnectionString -Account $ServiceAccount -CachePort $CachePort -ClusterPort $ClusterPort -ArbitrationPort $ArbitrationPort -ReplicationPort $ReplicationPort | |
Write-Verbose "Adding the cache host" | |
Add-CacheHost -Provider $Provider -ConnectionString $ConnectionString -Account $ServiceAccount | |
Add-CacheAdmin -Provider $Provider -ConnectionString $ConnectionString | |
Set-CacheHostConfig -IsLeadHost $true -HostName $CurrentHost -CachePort $CachePort | |
Set-CacheConnectionString -Provider $Provider -ConnectionString $ConnectionString | |
if ($NewCacheCluster) { | |
Write-Verbose "Starting cache cluster" | |
Start-CacheCluster | |
} else { | |
Write-Verbose "Starting cache host" | |
Start-CacheHost -hostname $CurrentHost -cacheport $CachePort | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment