Added base64 encryption of the script files. Now script files are stored
separately from templates in the ./scripts/ directory. test_windows_agent.py updated. Formatting in windows_agent.py fixed. Tracing and error capturing added to Set-LocalUserPassword. Function Set-LocalUserPassword removed as it is implemented by workflow means. Scripts now stored in one common folder. Scripts updated from murano-deployment. Templates updated to reflect new script names. Workflow template can reference multiple script files. These script files will be concatinated and encrypted with base64. Change-Id: Icb3532d2fb724bbb711c06086cb906bde22a380f Fixed flake8 reported issues. Change-Id: Icb3532d2fb724bbb711c06086cb906bde22a380f Fixed test failure after reformatting. Change-Id: Icb3532d2fb724bbb711c06086cb906bde22a380f Fixed issues with test. Revert back path selection for scripts folder. It is reasonable to have an ability to use own folder for custom workflows so script path is relative to template location. Change-Id: Icb3532d2fb724bbb711c06086cb906bde22a380f
This commit is contained in:
parent
8f7b38049a
commit
30b9bed2fc
@ -1,6 +1,6 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"ZnVuY3Rpb24gR2V0LURuc0xpc3RlbmluZ0lwQWRkcmVzcyB7DQogICAgSW1wb3J0LU1vZHVsZSBEbnNTZXJ2ZXINCiAgICAoR2V0LUROU1NlcnZlciAtQ29tcHV0ZXJOYW1lIGxvY2FsaG9zdCkuU2VydmVyU2V0dGluZy5MaXN0ZW5pbmdJcEFkZHJlc3MgfA0KICAgICAgICBXaGVyZS1PYmplY3QgeyAkXyAtbWF0Y2ggIlxkezEsM31cLlxkezEsM31cLlxkezEsM31cLlxkezEsM30iIH0NCn0NCg=="
|
||||
"Get-DnsListeningIpAddress.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
|
@ -1,21 +1,16 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"RnVuY3Rpb24gU2V0LUxvY2FsVXNlclBhc3N3b3JkIHsKICAgIHBhcmFtICgKICAgICAgICBbU3RyaW5nXSAkVXNlck5hbWUsCiAgICAgICAgW1N0cmluZ10gJFBhc3N3b3JkLAogICAgICAgIFtTd2l0Y2hdICRGb3JjZQogICAgKQogICAgCiAgICB0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQogICAgCiAgICBpZiAoKEdldC1XbWlPYmplY3QgV2luMzJfVXNlckFjY291bnQgLUZpbHRlciAiTG9jYWxBY2NvdW50ID0gJ1RydWUnIEFORCBOYW1lPSckVXNlck5hbWUnIikgLWVxICRudWxsKSB7CiAgICAgICAgdGhyb3cgIlVuYWJsZSB0byBmaW5kIGxvY2FsIHVzZXIgYWNjb3VudCAnJFVzZXJOYW1lJyIKICAgIH0KICAgIAogICAgaWYgKCRGb3JjZSkgewogICAgICAgIFdyaXRlLUxvZyAiQ2hhbmdpbmcgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScgdG8gJyoqKioqJyIgIyA6KQogICAgICAgIChbQURTSV0gIldpbk5UOi8vLi8kVXNlck5hbWUiKS5TZXRQYXNzd29yZCgkUGFzc3dvcmQpCiAgICB9CiAgICBlbHNlIHsKICAgICAgICBXcml0ZS1Mb2dXYXJuaW5nICJZb3UgYXJlIHRyeWluZyB0byBjaGFuZ2UgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScuIFRvIGRvIHRoaXMgcGxlYXNlIHJ1biB0aGUgY29tbWFuZCBhZ2FpbiB3aXRoIC1Gb3JjZSBwYXJhbWV0ZXIuIgogICAgICAgICRVc2VyQWNjb3VudAogICAgfQp9CgoKCkZ1bmN0aW9uIEluc3RhbGwtUm9sZVByaW1hcnlEb21haW5Db250cm9sbGVyCnsKPCMKLlNZTk9QU0lTCkNvbmZpZ3VyZSBub2RlJ3MgbmV0d29yayBhZGFwdGVycy4KQ3JlYXRlIGZpcnN0IGRvbWFpbiBjb250cm9sbGVyIGluIHRoZSBmb3Jlc3QuCgouRVhBTVBMRQpQUz4gSW5zdGFsbC1Sb2xlUHJpbWFyeURvbWFpbkNvbnRyb2xsZXIgLURvbWFpbk5hbWUgYWNtZS5sb2NhbCAtU2FmZU1vZGVQYXNzd29yZCAiUEBzc3cwcmQiCgpJbnN0YWxsIEROUyBhbmQgQUREUywgY3JlYXRlIGZvcmVzdCBhbmQgZG9tYWluICdhY21lLmxvY2FsJy4KU2V0IERDIHJlY292ZXJ5IG1vZGUgcGFzc3dvcmQgdG8gJ1BAc3N3MHJkJy4KIz4KCQoJcGFyYW0KCSgKCQlbU3RyaW5nXQoJCSMgTmV3IGRvbWFpbiBuYW1lLgoJCSREb21haW5OYW1lLAoJCQoJCVtTdHJpbmddCgkJIyBEb21haW4gY29udHJvbGxlciByZWNvdmVyeSBtb2RlIHBhc3N3b3JkLgoJCSRTYWZlTW9kZVBhc3N3b3JkCgkpCgoJdHJhcCB7IFN0b3AtRXhlY3V0aW9uICRfIH0KCiAgICAgICAgIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcwoJQWRkLVdpbmRvd3NGZWF0dXJlV3JhcHBlciBgCgkJLU5hbWUgIkROUyIsIkFELURvbWFpbi1TZXJ2aWNlcyIsIlJTQVQtREZTLU1nbXQtQ29uIiBgCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYAogICAgICAgIC1Ob3RpZnlSZXN0YXJ0CgoKCVdyaXRlLUxvZyAiQ3JlYXRpbmcgZmlyc3QgZG9tYWluIGNvbnRyb2xsZXIgLi4uIgoJCQoJJFNNQVAgPSBDb252ZXJ0VG8tU2VjdXJlU3RyaW5nIC1TdHJpbmcgJFNhZmVNb2RlUGFzc3dvcmQgLUFzUGxhaW5UZXh0IC1Gb3JjZQoJCQoJSW5zdGFsbC1BRERTRm9yZXN0IGAKCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgCgkJLVNhZmVNb2RlQWRtaW5pc3RyYXRvclBhc3N3b3JkICRTTUFQIGAKCQktRG9tYWluTW9kZSBEZWZhdWx0IGAKCQktRm9yZXN0TW9kZSBEZWZhdWx0IGAKCQktTm9SZWJvb3RPbkNvbXBsZXRpb24gYAoJCS1Gb3JjZSBgCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbAoKCVdyaXRlLUxvZyAiV2FpdGluZyBmb3IgcmVib290IC4uLiIJCQojCVN0b3AtRXhlY3V0aW9uIC1FeGl0Q29kZSAzMDEwIC1FeGl0U3RyaW5nICJDb21wdXRlciBtdXN0IGJlIHJlc3RhcnRlZCB0byBmaW5pc2ggZG9tYWluIGNvbnRyb2xsZXIgcHJvbW90aW9uLiIKIwlXcml0ZS1Mb2cgIlJlc3RhcmluZyBjb21wdXRlciAuLi4iCiMJUmVzdGFydC1Db21wdXRlciAtRm9yY2UKfQo="
|
||||
"ImportCoreFunctions.ps1",
|
||||
"Install-RolePrimaryDomainController.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Import-Module",
|
||||
"Arguments": {
|
||||
"Name": "CoreFunctions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Install-RolePrimaryDomainController",
|
||||
"Arguments": {
|
||||
"DomainName": "$domain",
|
||||
"SafeModePassword": "$recoveryPassword"
|
||||
}
|
||||
}
|
||||
{
|
||||
"Name": "Install-RolePrimaryDomainController",
|
||||
"Arguments": {
|
||||
"DomainName": "$domain",
|
||||
"SafeModePassword": "$recoveryPassword"
|
||||
}
|
||||
}
|
||||
],
|
||||
"RebootOnCompletion": 1
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"RnVuY3Rpb24gSW5zdGFsbC1Sb2xlU2Vjb25kYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KSW5zdGFsbCBhZGRpdGlvbmFsIChzZWNvbmRhcnkpIGRvbWFpbiBjb250cm9sbGVyLg0KDQojPg0KCXBhcmFtDQoJKA0KCQlbU3RyaW5nXQ0KCQkjIERvbWFpbiBuYW1lIHRvIGpvaW4gdG8uDQoJCSREb21haW5OYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBEb21haW4gdXNlciB3aG8gaXMgYWxsb3dlZCB0byBqb2luIGNvbXB1dGVyIHRvIGRvbWFpbi4NCgkJJFVzZXJOYW1lLA0KCQkNCgkJW1N0cmluZ10NCgkJIyBVc2VyJ3MgcGFzc3dvcmQuDQoJCSRQYXNzd29yZCwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KCQ0KCSRDcmVkZW50aWFsID0gTmV3LUNyZWRlbnRpYWwgLVVzZXJOYW1lICIkRG9tYWluTmFtZVwkVXNlck5hbWUiIC1QYXNzd29yZCAkUGFzc3dvcmQNCgkJDQoJIyBBZGQgcmVxdWlyZWQgd2luZG93cyBmZWF0dXJlcw0KCUFkZC1XaW5kb3dzRmVhdHVyZVdyYXBwZXIgYA0KCQktTmFtZSAiRE5TIiwiQUQtRG9tYWluLVNlcnZpY2VzIiwiUlNBVC1ERlMtTWdtdC1Db24iIGANCgkJLUluY2x1ZGVNYW5hZ2VtZW50VG9vbHMgYA0KICAgICAgICAgICAgICAgIC1Ob3RpZnlSZXN0YXJ0DQoJCQ0KCQ0KICAgICAgICBXcml0ZS1Mb2cgIkFkZGluZyBzZWNvbmRhcnkgZG9tYWluIGNvbnRyb2xsZXIgLi4uIg0KICAgIA0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCg0KCUluc3RhbGwtQUREU0RvbWFpbkNvbnRyb2xsZXIgYA0KCQktRG9tYWluTmFtZSAkRG9tYWluTmFtZSBgDQoJCS1TYWZlTW9kZUFkbWluaXN0cmF0b3JQYXNzd29yZCAkU01BUCBgDQoJCS1DcmVkZW50aWFsICRDcmVkZW50aWFsIGANCgkJLU5vUmVib290T25Db21wbGV0aW9uIGANCgkJLUZvcmNlIGANCgkJLUVycm9yQWN0aW9uIFN0b3AgfCBPdXQtTnVsbA0KDQoJV3JpdGUtTG9nICJXYWl0aW5nIGZvciByZXN0YXJ0IC4uLiINCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcnRpbmcgY29tcHV0ZXIgLi4uIg0KIwlSZXN0YXJ0LUNvbXB1dGVyIC1Gb3JjZQ0KfQ0K"
|
||||
"ImportCoreFunctions.ps1",
|
||||
"Install-RoleSecondaryDomainController.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Import-Module",
|
||||
"Arguments": {
|
||||
"Name": "CoreFunctions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Install-RoleSecondaryDomainController",
|
||||
"Arguments": {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,14 +1,10 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"ZnVuY3Rpb24gQ29weS1QcmVyZXF1aXNpdGVzIHsNCglwYXJhbSAoDQoJCVtTdHJpbmddICRQYXRoID0gJycsDQoJCVtTdHJpbmddICREZXN0aW5hdGlvbiA9ICcnDQoJKQ0KDQoJV3JpdGUtTG9nICItLT4gQ29weS1QcmVyZXF1aXNpdGVzIg0KDQogICAgaWYgKCREZXN0aW5hdGlvbiAtZXEgJycpIHsNCiAgICAgICAgdGhyb3coIkNvcHktUHJlcmVxdWlzaXRlczogRGVzdGluYXRpb24gcGF0aCBub3Qgc3BlY2lmaWVkISIpDQogICAgfQ0KDQogICAgaWYgKCRQYXRoIC1lcSAnJykgew0KICAgICAgICAkUGF0aCA9IFtFbnZpcm9ubWVudF06OkdldEVudmlyb25tZW50VmFyaWFibGUoJ011cmFub0ZpbGVTaGFyZScpDQogICAgICAgIGlmICgkUGF0aCAtZXEgJG51bGwpIHsNCiAgICAgICAgICAgIHRocm93KCJDb3B5LVByZXJlcXVpc2l0ZXM6IFVuYWJsZSB0byBkZXRlcm1pbmUgc291cmNlIHBhdGggZm9yIHByZXJlcXVpc2l0ZXMuIikNCiAgICAgICAgfQ0KICAgIH0NCg0KCVdyaXRlLUxvZyAiQ3JlYXRpbmcgbmV3IFBTRHJpdmUgLi4uIg0KCU5ldy1QU0RyaXZlIC1OYW1lICdQJyAtUFNQcm92aWRlciAnRmlsZVN5c3RlbScgLVJvb3QgJFBhdGggfCBPdXQtTnVsbA0KCVdyaXRlLUxvZyAiQ3JlYXRpbmcgZGVzdGluYXRpb24gZm9sZGVyIC4uLiINCglOZXctSXRlbSAtUGF0aCAkRGVzdGluYXRpb24gLUl0ZW1UeXBlIENvbnRhaW5lciAtRm9yY2UgfCBPdXQtTnVsbA0KCVdyaXRlLUxvZyAiQ29weWluZyBpdGVtcyAuLi4iDQoJQ29weS1JdGVtIC1QYXRoICdQOlxQcmVyZXF1aXNpdGVzXElJUycgLURlc3RpbmF0aW9uICREZXN0aW5hdGlvbiAtUmVjdXJzZSAtRm9yY2UgfCBPdXQtTnVsbA0KCVdyaXRlLUxvZyAiUmVtb3ZpbmcgUFNEcml2ZSAuLi4iDQoJUmVtb3ZlLVBTRHJpdmUgLU5hbWUgJ1AnIC1QU1Byb3ZpZGVyICdGaWxlU3lzdGVtJyAtRm9yY2UgfCBPdXQtTnVsbA0KCQ0KCVdyaXRlLUxvZyAiPC0tIENvcHktUHJlcmVxdWlzaXRlcyINCn0NCg0KDQoNCmZ1bmN0aW9uIEluc3RhbGwtV2ViU2VydmVyIHsNCglwYXJhbSAoDQoJCVtTdHJpbmddICRQcmVyZXF1aXNpdGVzUGF0aA0KCSkNCgkNCglXcml0ZS1Mb2cgIi0tPiBJbnN0YWxsLVdlYlNlcnZlciINCg0KCSRGZWF0dXJlTGlzdCA9IEAoDQoJCSdXZWItU2VydmVyJywNCgkJJ1dlYi1OZXQtRXh0NDUnLA0KCQknV2ViLUFTUCcsDQoJCSdXZWItQXNwLU5ldDQ1JywNCgkJJ1dlYi1JU0FQSS1FeHQnLA0KCQknV2ViLUlTQVBJLUZpbHRlcicsDQoJCSdXZWItSW5jbHVkZXMnDQoJKQ0KCQ0KCSRQcmVyZXF1aXNpdGVzTGlzdCA9IEAoDQoJCSdBc3BOZXRNdmM0U2V0dXAuZXhlJywNCgkJJ1dlYkFwcGxpY2F0aW9ucy5leGUnDQoJKQ0KICAgIA0KCSRQcmVyZXF1aXNpdGVzUGF0aCA9IFtJTy5QYXRoXTo6Q29tYmluZSgkUHJlcmVxdWlzaXRlc1BhdGgsICdJSVMnKQ0KICAgIA0KCVdyaXRlLUxvZyAiVmFsaWRhdGluZyBwcmVyZXF1aXNpdGVzIGJhc2VkIG9uIHRoZSBsaXN0IC4uLiINCglmb3JlYWNoICgkRmlsZU5hbWUgaW4gJFByZXJlcXVpc2l0ZXNMaXN0KSB7DQoJCSRGaWxlUGF0aCA9IFtJTy5QYXRoXTo6Q29tYmluZSgkUHJlcmVxdWlzaXRlc1BhdGgsICRGaWxlTmFtZSkNCgkJaWYgKC1ub3QgKFRlc3QtUGF0aCAtUGF0aCAkRmlsZVBhdGggLVBhdGhUeXBlIExlYWYpKSB7DQoJCQl0aHJvdygiUHJlcmVxdWlzaXRlIGZpbGUgbm90IGZvdW5kOiAnJEZpbGVQYXRoJyIpDQoJCX0NCgl9DQoJDQoJSW1wb3J0LU1vZHVsZSBTZXJ2ZXJNYW5hZ2VyDQoJDQoJV3JpdGUtTG9nICJJbnN0YWxsaW5nIFdlYiBTZXJ2ZXIgLi4uIg0KCUluc3RhbGwtV2luZG93c0ZlYXR1cmUgJEZlYXR1cmVMaXN0IC1JbmNsdWRlTWFuYWdlbWVudFRvb2xzDQoJDQoJV3JpdGUtTG9nICJJbnN0YWxsaW5nIEFzcE5ldE12cDQgLi4uIg0KCSRFeGVjID0gRXhlYyAtRmlsZVBhdGggJChbSU8uUGF0aF06OkNvbWJpbmUoJFByZXJlcXVpc2l0ZXNQYXRoLCAnQXNwTmV0TXZjNFNldHVwLmV4ZScpKSAtQXJndW1lbnRMaXN0ICcvcScgLVBhc3NUaHJ1DQoJaWYgKCRFeGVjLkV4aXRDb2RlIC1uZSAwKSB7DQoJCXRocm93KCJJbnN0YWxsYXRpb24gb2YgJ0FzcE5ldE12YzRTZXR1cC5leGUnIGZhaWxlZC4gUHJvY2VzcyBleGl0IGNvZGUgJyQoJEV4ZWMuRXhpdENvZGUpJyIpDQoJfQ0KCQ0KCSMgRXh0cmFjdCBXZWJBcHBsaWNhdGlvbnMgZm9sZGVyIHdpdGggKi50YXJnZXQgZmlsZXMgdG8NCgkjICAgQzpcUHJvZ3JhbSBGaWxlcyAoeDg2KVxNU0J1aWxkXE1pY3Jvc29mdFxWaXN1YWxTdHVkaW9cdjEwLjANCglXcml0ZS1Mb2cgIkluc3RhbGxpbmcgV2ViQXBwbGljYXRpb24gdGFyZ2V0cyAuLi4iDQoJJFdlYkFwcGxpY2F0aW9uc1RhcmdldHNSb290ID0gJ0M6XFByb2dyYW0gRmlsZXMgKHg4NilcTVNCdWlsZFxNaWNyb3NvZnRcVmlzdWFsU3R1ZGlvXHYxMC4wJw0KCSRudWxsID0gTmV3LUl0ZW0gLVBhdGggJFdlYkFwcGxpY2F0aW9uc1RhcmdldHNSb290IC1JdGVtVHlwZSBDb250YWluZXINCgkkRXhlYyA9IEV4ZWMgLUZpbGVQYXRoICQoW0lPLlBhdGhdOjpDb21iaW5lKCRQcmVyZXF1aXNpdGVzUGF0aCwgJ1dlYkFwcGxpY2F0aW9ucy5leGUnKSkgLUFyZ3VtZW50TGlzdCBAKCItb2AiJFdlYkFwcGxpY2F0aW9uc1RhcmdldHNSb290YCIiLCAnLXknKSAtUGFzc1RocnUNCglpZiAoJEV4ZWMuRXhpdENvZGUgLW5lIDApIHsNCgkJdGhyb3coIkluc3RhbGxhdGlvbiBvZiAnV2ViQXBwbGljYXRpb25zLmV4ZScgZmFpbGVkLiBQcm9jZXNzIGV4aXQgY29kZSAnJCgkRXhlYy5FeGl0Q29kZSknIikNCgl9DQoNCglXcml0ZS1Mb2cgIjwtLSBJbnN0YWxsLVdlYlNlcnZlciINCn0NCg0K"
|
||||
"ImportCoreFunctions.ps1",
|
||||
"CopyPrerequisites.ps1",
|
||||
"InstallIIS.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Import-Module",
|
||||
"Arguments": {
|
||||
"Name": "CoreFunctions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Copy-Prerequisites",
|
||||
"Arguments": {
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,11 +1,9 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"ImportCoreFunctions.ps1",
|
||||
"Join-Domain.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Import-Module",
|
||||
"Arguments": {
|
||||
"Name": "CoreFunctions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Set-NetworkAdapterConfiguration",
|
||||
"Arguments": {
|
||||
@ -23,8 +21,5 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"RebootOnCompletion": 1,
|
||||
"Scripts": [
|
||||
"RnVuY3Rpb24gSm9pbi1Eb21haW4gewo8IwouU1lOT1BTSVMKRXhlY3V0ZXMgIkpvaW4gZG9tYWluIiBhY3Rpb24uCgpSZXF1aXJlcyAnQ29yZUZ1bmN0aW9ucycgbW9kdWxlLgojPgoJcGFyYW0gKAoJCVtTdHJpbmddICREb21haW5OYW1lID0gJycsCgkJW1N0cmluZ10gJFVzZXJOYW1lID0gJycsCgkJW1N0cmluZ10gJFBhc3N3b3JkID0gJycsCgkJW1N0cmluZ10gJE9VUGF0aCA9ICcnLAogICAgICAgIFtTd2l0Y2hdICRBbGxvd1Jlc3RhcnQKCSkKCQoJaWYgKCRVc2VyTmFtZSAtZXEgJycpIHsKCQkkVXNlck5hbWUgPSAnQWRtaW5pc3RyYXRvcicKCX0KCgkkQ3JlZGVudGlhbCA9IE5ldy1DcmVkZW50aWFsIC1Vc2VyTmFtZSAiJERvbWFpbk5hbWVcJFVzZXJOYW1lIiAtUGFzc3dvcmQgJFBhc3N3b3JkCgoJaWYgKFRlc3QtQ29tcHV0ZXJOYW1lIC1Eb21haW5OYW1lICREb21haW5OYW1lKSB7CiAgICAgICAgV3JpdGUtTG9nV2FybmluZyAiQ29tcHV0ZXIgYWxyZWFkeSBqb2luZWQgdG8gZG9tYWluICckRG9tYWluTmFtZSciCgl9CgllbHNlIHsKCQlXcml0ZS1Mb2cgIkpvaW5pbmcgY29tcHV0ZXIgdG8gZG9tYWluICckRG9tYWluTmFtZScgLi4uIgoJCQoJCWlmICgkT1VQYXRoIC1lcSAnJykgewoJCQlBZGQtQ29tcHV0ZXIgLURvbWFpbk5hbWUgJERvbWFpbk5hbWUgLUNyZWRlbnRpYWwgJENyZWRlbnRpYWwgLUZvcmNlIC1FcnJvckFjdGlvbiBTdG9wCgkJfQoJCWVsc2UgewoJCQlBZGQtQ29tcHV0ZXIgLURvbWFpbk5hbWUgJERvbWFpbk5hbWUgLUNyZWRlbnRpYWwgJENyZWRlbnRpYWwgLU9VUGF0aCAkT1VQYXRoIC1Gb3JjZSAtRXJyb3JBY3Rpb24gU3RvcAoJCX0KCQkKICAgICAgICBpZiAoJEFsbG93UmVzdGFydCkgewogICAgICAgICAgICBXcml0ZS1Mb2cgIlJlc3RhcnRpbmcgY29tcHV0ZXIgLi4uIgogICAgICAgICAgICBSZXN0YXJ0LUNvbXB1dGVyIC1Gb3JjZQogICAgICAgIH0KICAgICAgICBlbHNlIHsKICAgICAgICAgICAgV3JpdGUtTG9nICJQbGVhc2UgcmVzdGFydCB0aGUgY29tcHV0ZXIgbm93LiIKICAgICAgICB9Cgl9Cn0K"
|
||||
]
|
||||
"RebootOnCompletion": 1
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"Scripts": [],
|
||||
"Commands": [],
|
||||
"RebootOnCompletion": 0,
|
||||
"Scripts": []
|
||||
}
|
@ -1,14 +1,9 @@
|
||||
{
|
||||
"Scripts": [
|
||||
"RnVuY3Rpb24gU2V0LUxvY2FsVXNlclBhc3N3b3JkIHsNCiAgICBwYXJhbSAoDQogICAgICAgIFtTdHJpbmddICRVc2VyTmFtZSwNCiAgICAgICAgW1N0cmluZ10gJFBhc3N3b3JkLA0KICAgICAgICBbU3dpdGNoXSAkRm9yY2UNCiAgICApDQogICAgDQogICAgdHJhcCB7IFN0b3AtRXhlY3V0aW9uICRfIH0NCiAgICANCiAgICBpZiAoKEdldC1XbWlPYmplY3QgV2luMzJfVXNlckFjY291bnQgLUZpbHRlciAiTG9jYWxBY2NvdW50ID0gJ1RydWUnIEFORCBOYW1lPSckVXNlck5hbWUnIikgLWVxICRudWxsKSB7DQogICAgICAgIHRocm93ICJVbmFibGUgdG8gZmluZCBsb2NhbCB1c2VyIGFjY291bnQgJyRVc2VyTmFtZSciDQogICAgfQ0KICAgIA0KICAgIGlmICgkRm9yY2UpIHsNCiAgICAgICAgV3JpdGUtTG9nICJDaGFuZ2luZyBwYXNzd29yZCBmb3IgdXNlciAnJFVzZXJOYW1lJyB0byAnKioqKionIiAjIDopDQogICAgICAgIChbQURTSV0gIldpbk5UOi8vLi8kVXNlck5hbWUiKS5TZXRQYXNzd29yZCgkUGFzc3dvcmQpDQogICAgfQ0KICAgIGVsc2Ugew0KICAgICAgICBXcml0ZS1Mb2dXYXJuaW5nICJZb3UgYXJlIHRyeWluZyB0byBjaGFuZ2UgcGFzc3dvcmQgZm9yIHVzZXIgJyRVc2VyTmFtZScuIFRvIGRvIHRoaXMgcGxlYXNlIHJ1biB0aGUgY29tbWFuZCBhZ2FpbiB3aXRoIC1Gb3JjZSBwYXJhbWV0ZXIuIg0KICAgICAgICAkVXNlckFjY291bnQNCiAgICB9DQp9DQoNCg0KDQpGdW5jdGlvbiBJbnN0YWxsLVJvbGVQcmltYXJ5RG9tYWluQ29udHJvbGxlcg0Kew0KPCMNCi5TWU5PUFNJUw0KQ29uZmlndXJlIG5vZGUncyBuZXR3b3JrIGFkYXB0ZXJzLg0KQ3JlYXRlIGZpcnN0IGRvbWFpbiBjb250cm9sbGVyIGluIHRoZSBmb3Jlc3QuDQoNCi5FWEFNUExFDQpQUz4gSW5zdGFsbC1Sb2xlUHJpbWFyeURvbWFpbkNvbnRyb2xsZXIgLURvbWFpbk5hbWUgYWNtZS5sb2NhbCAtU2FmZU1vZGVQYXNzd29yZCAiUEBzc3cwcmQiDQoNCkluc3RhbGwgRE5TIGFuZCBBRERTLCBjcmVhdGUgZm9yZXN0IGFuZCBkb21haW4gJ2FjbWUubG9jYWwnLg0KU2V0IERDIHJlY292ZXJ5IG1vZGUgcGFzc3dvcmQgdG8gJ1BAc3N3MHJkJy4NCiM+DQoJDQoJcGFyYW0NCgkoDQoJCVtTdHJpbmddDQoJCSMgTmV3IGRvbWFpbiBuYW1lLg0KCQkkRG9tYWluTmFtZSwNCgkJDQoJCVtTdHJpbmddDQoJCSMgRG9tYWluIGNvbnRyb2xsZXIgcmVjb3ZlcnkgbW9kZSBwYXNzd29yZC4NCgkJJFNhZmVNb2RlUGFzc3dvcmQNCgkpDQoNCgl0cmFwIHsgU3RvcC1FeGVjdXRpb24gJF8gfQ0KDQogICAgICAgICMgQWRkIHJlcXVpcmVkIHdpbmRvd3MgZmVhdHVyZXMNCglBZGQtV2luZG93c0ZlYXR1cmVXcmFwcGVyIGANCgkJLU5hbWUgIkROUyIsIkFELURvbWFpbi1TZXJ2aWNlcyIsIlJTQVQtREZTLU1nbXQtQ29uIiBgDQoJCS1JbmNsdWRlTWFuYWdlbWVudFRvb2xzIGANCiAgICAgICAgLU5vdGlmeVJlc3RhcnQNCg0KDQoJV3JpdGUtTG9nICJDcmVhdGluZyBmaXJzdCBkb21haW4gY29udHJvbGxlciAuLi4iDQoJCQ0KCSRTTUFQID0gQ29udmVydFRvLVNlY3VyZVN0cmluZyAtU3RyaW5nICRTYWZlTW9kZVBhc3N3b3JkIC1Bc1BsYWluVGV4dCAtRm9yY2UNCgkJDQoJSW5zdGFsbC1BRERTRm9yZXN0IGANCgkJLURvbWFpbk5hbWUgJERvbWFpbk5hbWUgYA0KCQktU2FmZU1vZGVBZG1pbmlzdHJhdG9yUGFzc3dvcmQgJFNNQVAgYA0KCQktRG9tYWluTW9kZSBEZWZhdWx0IGANCgkJLUZvcmVzdE1vZGUgRGVmYXVsdCBgDQoJCS1Ob1JlYm9vdE9uQ29tcGxldGlvbiBgDQoJCS1Gb3JjZSBgDQoJCS1FcnJvckFjdGlvbiBTdG9wIHwgT3V0LU51bGwNCg0KCVdyaXRlLUhvc3QgIldhaXRpbmcgZm9yIHJlYm9vdCAuLi4iCQkNCiMJU3RvcC1FeGVjdXRpb24gLUV4aXRDb2RlIDMwMTAgLUV4aXRTdHJpbmcgIkNvbXB1dGVyIG11c3QgYmUgcmVzdGFydGVkIHRvIGZpbmlzaCBkb21haW4gY29udHJvbGxlciBwcm9tb3Rpb24uIg0KIwlXcml0ZS1Mb2cgIlJlc3RhcmluZyBjb21wdXRlciAuLi4iDQojCVJlc3RhcnQtQ29tcHV0ZXIgLUZvcmNlDQp9DQo="
|
||||
"ImportCoreFunctions.ps1",
|
||||
"Set-LocalUserPasswordPassword.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Import-Module",
|
||||
"Arguments": {
|
||||
"Name": "CoreFunctions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Set-LocalUserPassword",
|
||||
"Arguments": {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
50
data/templates/agent/scripts/CopyPrerequisites.ps1
Normal file
50
data/templates/agent/scripts/CopyPrerequisites.ps1
Normal file
@ -0,0 +1,50 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Copy-Prerequisites {
|
||||
param (
|
||||
[String] $Path = '',
|
||||
[String] $Destination = ''
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "--> Copy-Prerequisites"
|
||||
|
||||
if ($Destination -eq '') {
|
||||
throw("Copy-Prerequisites: Destination path not specified!")
|
||||
}
|
||||
|
||||
if ($Path -eq '') {
|
||||
$Path = [Environment]::GetEnvironmentVariable('MuranoFileShare')
|
||||
if ($Path -eq $null) {
|
||||
throw("Copy-Prerequisites: Unable to determine source path for prerequisites.")
|
||||
}
|
||||
}
|
||||
|
||||
Write-Log "Creating new PSDrive ..."
|
||||
New-PSDrive -Name 'P' -PSProvider 'FileSystem' -Root $Path | Out-Null
|
||||
|
||||
Write-Log "Creating destination folder ..."
|
||||
New-Item -Path $Destination -ItemType Container -Force | Out-Null
|
||||
|
||||
Write-Log "Copying items ..."
|
||||
Copy-Item -Path 'P:\Prerequisites\IIS' -Destination $Destination -Recurse -Force | Out-Null
|
||||
|
||||
Write-Log "Removing PSDrive ..."
|
||||
Remove-PSDrive -Name 'P' -PSProvider 'FileSystem' -Force | Out-Null
|
||||
|
||||
Write-Log "<-- Copy-Prerequisites"
|
||||
}
|
||||
}
|
148
data/templates/agent/scripts/DeployWebApp.ps1
Normal file
148
data/templates/agent/scripts/DeployWebApp.ps1
Normal file
@ -0,0 +1,148 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Register-WebApp {
|
||||
<#
|
||||
.LINKS
|
||||
|
||||
http://www.iis.net/learn/manage/powershell/powershell-snap-in-creating-web-sites-web-applications-virtual-directories-and-application-pools
|
||||
#>
|
||||
param (
|
||||
[String] $Source,
|
||||
[String] $Path = "C:\inetpub\wwwroot",
|
||||
[String] $Name = "",
|
||||
[String] $Username = "",
|
||||
[String] $Password = ""
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Import-Module WebAdministration
|
||||
|
||||
if ($Name -eq "") {
|
||||
$Name = @([IO.Path]::GetDirectoryName($Source) -split '\\')[-1]
|
||||
if ($Name -eq "wwwroot") {
|
||||
throw("Application pool name couldn't be 'wwwroot'.")
|
||||
}
|
||||
}
|
||||
else {
|
||||
$Path = [IO.Path]::Combine($Path, $Name)
|
||||
}
|
||||
|
||||
Copy-Item -Path $Source -Destination $Path -Recurse -Force
|
||||
|
||||
|
||||
# Create new application pool
|
||||
$AppPool = New-WebAppPool -Name $Name -Force
|
||||
#$AppPool = Get-Item "IIS:\AppPools\$Name"
|
||||
$AppPool.managedRuntimeVersion = 'v4.0'
|
||||
$AppPool.managedPipelineMode = 'Classic'
|
||||
$AppPool.processModel.loadUserProfile = $true
|
||||
$AppPool.processModel.logonType = 'LogonBatch'
|
||||
|
||||
#Set Identity type
|
||||
if ($Username -eq "") {
|
||||
$AppPool.processModel.identityType = 'ApplicationPoolIdentity'
|
||||
}
|
||||
else {
|
||||
$AppPool.processModel.identityType = 'SpecificUser'
|
||||
$AppPool.processModel.userName = $Username
|
||||
$AppPool.processModel.password = $Password
|
||||
$AppPool | Set-Item
|
||||
}
|
||||
|
||||
|
||||
# Create Website
|
||||
$WebSite = New-WebSite -Name $Name -Port 80 -HostHeader $Name -PhysicalPath $Path -Force
|
||||
#$WebSite = Get-Item "IIS:\Sites\$Name"
|
||||
|
||||
# Set the Application Pool
|
||||
Set-ItemProperty "IIS:\Sites\$Name" 'ApplicationPool' $Name
|
||||
|
||||
#Turn on Directory Browsing
|
||||
#Set-WebConfigurationProperty -Filter '/system.webServer/directoryBrowse' -Name 'enabled' -Value $true -PSPath "IIS:\Sites\$Name"
|
||||
|
||||
# Update Authentication
|
||||
#Set-WebConfigurationProperty -Filter '/system.WebServer/security/authentication/AnonymousAuthentication' -Name 'enabled' -Value $true -Location $name
|
||||
#Set-WebConfigurationProperty -Filter '/system.WebServer/security/authentication/windowsAuthentication' -Name 'enabled' -Value $false -Location $Name
|
||||
#Set-WebConfigurationProperty -Filter '/system.WebServer/security/authentication/basicAuthentication' -Name 'enabled' -Value $false -Location $Name
|
||||
|
||||
$WebSite.Start()
|
||||
|
||||
Add-Content -Path "C:\Windows\System32\Drivers\etc\hosts" -Value "127.0.0.1 $Name"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Function Deploy-WebAppFromGit {
|
||||
param (
|
||||
[String] $URL,
|
||||
[String] $TempPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName()),
|
||||
[String] $OutputPath = [IO.Path]::Combine([IO.Path]::GetTempPath(), [IO.Path]::GetRandomFileName())
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "TempPath = '$TempPath'"
|
||||
Write-Log "OutputPath = '$OutputPath'"
|
||||
|
||||
|
||||
# Fetch web application
|
||||
#----------------------
|
||||
Write-Log "Fetching sources from Git ..."
|
||||
|
||||
$null = New-Item -Path $TempPath -ItemType Container
|
||||
Exec -FilePath 'git.exe' -ArgumentList @('clone', $URL) -WorkingDir $TempPath -RedirectStreams
|
||||
|
||||
$Path = @(Get-ChildItem $TempPath)[0].FullName
|
||||
#----------------------
|
||||
|
||||
|
||||
# Build web application
|
||||
#----------------------
|
||||
Write-Log "Building sources ..."
|
||||
|
||||
$msbuild = "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe"
|
||||
|
||||
$null = New-Item -Path $OutputPath -ItemType Container
|
||||
|
||||
$SlnFiles = @(Get-ChildItem -Path $Path -Filter *.sln -Recurse)
|
||||
|
||||
# Start new processs with additional env variables:
|
||||
#* VisualStudioVersion = "10.0"
|
||||
#* EnableNuGetPackageRestore = "true"
|
||||
Exec -FilePath $msbuild `
|
||||
-ArgumentList @($SlnFiles[0].FullName, "/p:OutputPath=$OutputPath") `
|
||||
-Environment @{'VisualStudioVersion' = '10.0'; 'EnableNuGetPackageRestore' = 'true'} `
|
||||
-RedirectStreams
|
||||
|
||||
$AppFolder = @(Get-ChildItem ([IO.Path]::Combine($OutputPath, '_PublishedWebsites')))[0]
|
||||
#----------------------
|
||||
|
||||
|
||||
# Install web application
|
||||
#------------------------
|
||||
Register-WebApp -Source $AppFolder.FullName -Name $AppFolder.Name
|
||||
#------------------------
|
||||
}
|
||||
}
|
61
data/templates/agent/scripts/Export-Function.ps1
Normal file
61
data/templates/agent/scripts/Export-Function.ps1
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
function Export-Function {
|
||||
param (
|
||||
[String[]] $Name,
|
||||
|
||||
[Parameter(ValueFromPipeline=$true)]
|
||||
[String] $Path = [IO.Path]::GetTempFileName(),
|
||||
|
||||
[Switch] $All
|
||||
)
|
||||
|
||||
if ([IO.Path]::GetExtension($Path) -ne 'ps1') {
|
||||
$null = Rename-Item -Path $Path -NewName "$Path.ps1" -Force
|
||||
$Path = "$Path.ps1"
|
||||
}
|
||||
|
||||
$SystemFunctions = @(
|
||||
'A:', 'B:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:',
|
||||
'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:',
|
||||
'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:',
|
||||
'cd..', 'cd\', 'help', 'mkdir', 'more', 'oss', 'prompt',
|
||||
'Clear-Host', 'Get-Verb', 'Pause', 'TabExpansion2'
|
||||
)
|
||||
|
||||
if ($All) {
|
||||
Get-ChildItem Function: |
|
||||
Where-Object {$_.ModuleName -eq ''} |
|
||||
Where-Object {$SystemFunctions -notcontains $_.Name} |
|
||||
ForEach-Object {
|
||||
Add-Content -Path $Path -Value @"
|
||||
|
||||
|
||||
function $($_.Name) {
|
||||
$($_.ScriptBlock)
|
||||
}
|
||||
|
||||
"@
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($FunctionName in $Name) {
|
||||
$FunctionObject = Get-ChildItem "Function:\$FunctionName"
|
||||
if ($FunctionObject -ne $null) {
|
||||
Add-Content -Path $Path -Value @"
|
||||
|
||||
|
||||
function $FunctionName {
|
||||
$($FunctionObject.ScriptBlock)
|
||||
}
|
||||
|
||||
"@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $Path
|
||||
}
|
239
data/templates/agent/scripts/Failover-Cluster.ps1
Normal file
239
data/templates/agent/scripts/Failover-Cluster.ps1
Normal file
@ -0,0 +1,239 @@
|
||||
<#
|
||||
.DESCRIPTION
|
||||
|
||||
## Failover Cluster Input Data (from the UI)
|
||||
|
||||
* Domain Membership
|
||||
- [String] / [Select box] $DomainName - Domain name
|
||||
* Domain User Credentials
|
||||
- [String] $UserName - Username
|
||||
- [Password string] $UserPassword - User password
|
||||
* Shared Folder Information
|
||||
- [String] $ShareServer - Server which will host the folder
|
||||
- [String] $ShareName - Share name
|
||||
- [String] $SharePath - Shared folder internal path
|
||||
* Failover Cluster Members
|
||||
- [String] $ClusterName - Cluster name
|
||||
- [String] $ClusterIP - Static IP address that will be assigned to the cluster
|
||||
- [String[]] $ClusterNodes - List of node names
|
||||
|
||||
|
||||
|
||||
## Failover Cluster creation workflow
|
||||
|
||||
* Create AD domain
|
||||
* Join all the VMs to that domain
|
||||
* Prepare nodes
|
||||
- Install Failover Cluster prerequisites on all FC nodes
|
||||
* Create failover cluster
|
||||
- Create new cluster
|
||||
- Add members
|
||||
* Confugure FC quorum
|
||||
- Create new folder that will be shared
|
||||
- Share that folder with appropriate permissions
|
||||
- Configure quorum mode
|
||||
|
||||
|
||||
|
||||
## Helpful SmbShare* Functions
|
||||
|
||||
* New-SmbShare
|
||||
* Grant-SmbShareAccess
|
||||
|
||||
#>
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Install-FailoverClusterPrerequisites {
|
||||
#Import-Module FailoverClusters
|
||||
|
||||
#Add-WindowsFeature Failover-Clustering, RSAT-Clustering-PowerShell
|
||||
}
|
||||
|
||||
|
||||
|
||||
function New-FailoverClusterSharedFolder {
|
||||
param (
|
||||
[String] $ClusterName,
|
||||
[String] $DomainName,
|
||||
[String] $ShareServer,
|
||||
[String] $SharePath = $($Env:SystemDrive + '\FCShare'),
|
||||
[String] $ShareName = 'FCShare',
|
||||
[String] $UserName,
|
||||
[String] $UserPassword,
|
||||
$Credential = $null
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "--> New-FailoverClusterSharedFolder"
|
||||
|
||||
Write-Log "Creating shared folder for Failover Cluster ..."
|
||||
|
||||
if ($Credential -eq $null) {
|
||||
$Credential = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
}
|
||||
|
||||
if ((Test-Connection -ComputerName $ShareServer -Count 1 -Quiet) -eq $false) {
|
||||
throw("Server '$ShareServer' is unreachable via ICMP.")
|
||||
}
|
||||
|
||||
$Session = New-PSSession -ComputerName $ShareServer -Credential $Credential
|
||||
|
||||
Write-Log "Creating folder on '$ShareServer' ..."
|
||||
Invoke-Command -Session $Session -ScriptBlock {
|
||||
param (
|
||||
[String] $SharePath,
|
||||
[String] $ShareName,
|
||||
[String] $ClusterAccount
|
||||
)
|
||||
|
||||
Remove-SmbShare -Name $ShareName -Force -ErrorAction 'SilentlyContinue'
|
||||
Remove-Item -Path $SharePath -Force -ErrorAction 'SilentlyContinue'
|
||||
|
||||
New-Item -Path $SharePath -ItemType Container -Force
|
||||
|
||||
New-SmbShare -Path $SharePath `
|
||||
-Name $ShareName `
|
||||
-FullAccess "$ClusterAccount", 'Everyone' `
|
||||
-Description "Shared folder for Failover Cluster."
|
||||
|
||||
} -ArgumentList $SharePath, $ShareName, "$DomainName\$ClusterName`$"
|
||||
|
||||
Write-Log "Confguring Failover Cluster to use shared folder as qourum resourse ..."
|
||||
|
||||
$null = Set-ClusterQuorum -NodeAndFileShareMajority "\\$ShareServer\$ShareName"
|
||||
|
||||
Write-Log "<-- New-FailoverClusterSharedFolder"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function New-FailoverCluster {
|
||||
param (
|
||||
[String] $ClusterName,
|
||||
[String] $StaticAddress,
|
||||
[String[]] $ClusterNodes,
|
||||
[String] $DomainName,
|
||||
[String] $UserName,
|
||||
[String] $UserPassword,
|
||||
$Credential
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "ClusterNodes: $($ClusterNodes -join ', ')"
|
||||
|
||||
if ($Credential -eq $null) {
|
||||
$Credential = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
}
|
||||
|
||||
foreach ($Node in $ClusterNodes) {
|
||||
Write-LogDebug "Installing Failover Cluster modules on '$Node' ..."
|
||||
$null = Invoke-Command `
|
||||
-ComputerName $Node `
|
||||
-Credential $Credential `
|
||||
-ScriptBlock {
|
||||
Add-WindowsFeature Failover-Clustering, RSAT-Clustering-PowerShell
|
||||
}
|
||||
}
|
||||
|
||||
Import-Module FailoverClusters
|
||||
|
||||
if ((Get-Cluster $ClusterName -ErrorAction SilentlyContinue) -eq $null) {
|
||||
Write-Log "Creating new cluster '$ClusterName' ..."
|
||||
<#
|
||||
Start-PowerShellProcess -Command @"
|
||||
Import-Module FailoverClusters
|
||||
New-Cluster -Name '$ClusterName' -StaticAddress '$StaticAddress'
|
||||
"@ -Credential $Credential -NoBase64
|
||||
#>
|
||||
New-Cluster -Name "$ClusterName" -StaticAddress "$StaticAddress"
|
||||
Start-Sleep -Seconds 30
|
||||
}
|
||||
else {
|
||||
Write-Log "Cluster '$ClusterName' already exists."
|
||||
}
|
||||
|
||||
foreach ($Node in $ClusterNodes) {
|
||||
Write-Log "Adding node '$Node' to the cluster '$ClusterName' ..."
|
||||
if ((Get-ClusterNode $Node -ErrorAction SilentlyContinue) -eq $null) {
|
||||
Write-Log "Adding node ..."
|
||||
<#
|
||||
Start-PowerShellProcess -Command @"
|
||||
Import-Module FailoverClusters
|
||||
Add-ClusterNode -Cluster '$ClusterName' -Name '$Node'
|
||||
"@ -Credential $Credential -NoBase64
|
||||
#>
|
||||
Add-ClusterNode -Cluster "$ClusterName" -Name "$Node"
|
||||
}
|
||||
else {
|
||||
Write-Log "Node '$Node' already a part of the cluster '$ClusterName'."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
<#
|
||||
|
||||
# Example
|
||||
|
||||
$DomainName = 'fc-acme.local'
|
||||
$DomainUser = 'Administrator'
|
||||
$DomainPassword = 'P@ssw0rd'
|
||||
|
||||
$ClusterName = 'fc-test'
|
||||
$ClusterIP = '10.200.0.60'
|
||||
$ClusterNodes = @('fc-node-01','fc-node-02','fc-node-03')
|
||||
|
||||
$ShareServer = 'fc-dc-01'
|
||||
$ShareName = 'FCShare'
|
||||
|
||||
$SharePath = "C:\$ShareName"
|
||||
|
||||
|
||||
|
||||
Import-Module CoreFunctions -Force
|
||||
|
||||
$Creds = New-Credential `
|
||||
-UserName "$DomainName\$DomainUser" `
|
||||
-Password "$DomainPassword"
|
||||
|
||||
New-FailoverCluster `
|
||||
-ClusterName $ClusterName `
|
||||
-StaticAddress $ClusterIP `
|
||||
-ClusterNodes $ClusterNodes `
|
||||
-Credential $Creds
|
||||
|
||||
New-FailoverClusterSharedFolder `
|
||||
-ClusterName $ClusterName `
|
||||
-DomainName $DomainName `
|
||||
-ShareServer $ShareServer `
|
||||
-SharePath "$SharePath" `
|
||||
-ShareName "$ShareName" `
|
||||
-Credential $Creds
|
||||
|
||||
#>
|
@ -0,0 +1,7 @@
|
||||
|
||||
function Get-DnsListeningIpAddress {
|
||||
Import-Module DnsServer
|
||||
|
||||
(Get-DNSServer -ComputerName localhost).ServerSetting.ListeningIpAddress |
|
||||
Where-Object { $_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" }
|
||||
}
|
65
data/templates/agent/scripts/ImportCoreFunctions.ps1
Normal file
65
data/templates/agent/scripts/ImportCoreFunctions.ps1
Normal file
@ -0,0 +1,65 @@
|
||||
Import-Module CoreFunctions -Force
|
||||
|
||||
|
||||
function Show-InvocationInfo {
|
||||
param (
|
||||
$Invocation,
|
||||
[Switch] $End
|
||||
)
|
||||
|
||||
if ($End) {
|
||||
Write-LogDebug "</function name='$($Invocation.MyCommand.Name)'>"
|
||||
}
|
||||
else {
|
||||
Write-LogDebug "<function name='$($Invocation.MyCommand.Name)'>"
|
||||
Write-LogDebug "<param>"
|
||||
foreach ($Parameter in $Invocation.MyCommand.Parameters) {
|
||||
foreach ($Key in $Parameter.Keys) {
|
||||
$Type = $Parameter[$Key].ParameterType.FullName
|
||||
foreach ($Value in $Invocation.BoundParameters[$Key]) {
|
||||
Write-LogDebug "[$Type] $Key = '$Value'"
|
||||
}
|
||||
}
|
||||
}
|
||||
Write-LogDebug "</param>"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$TrapHandler = {
|
||||
Write-LogError @("<exception>", $_) -EntireObject
|
||||
Write-LogError "</exception>"
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
|
||||
<#
|
||||
# Usage example for Show-InvocationInfo
|
||||
|
||||
function MyFunction {
|
||||
param (
|
||||
[String] $Value1,
|
||||
[String] $Value2,
|
||||
[Int] $Int1
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
# Main code here
|
||||
}
|
||||
}
|
||||
#>
|
@ -0,0 +1,43 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Install-RolePrimaryDomainController {
|
||||
param (
|
||||
[String] $DomainName,
|
||||
[String] $SafeModePassword
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Add-WindowsFeatureWrapper `
|
||||
-Name "DNS","AD-Domain-Services","RSAT-DFS-Mgmt-Con" `
|
||||
-IncludeManagementTools `
|
||||
-NotifyRestart
|
||||
|
||||
Write-Log "Creating first domain controller ..."
|
||||
|
||||
$SMAP = ConvertTo-SecureString -String $SafeModePassword -AsPlainText -Force
|
||||
|
||||
$null = Install-ADDSForest `
|
||||
-DomainName $DomainName `
|
||||
-SafeModeAdministratorPassword $SMAP `
|
||||
-DomainMode Default `
|
||||
-ForestMode Default `
|
||||
-NoRebootOnCompletion `
|
||||
-Force
|
||||
|
||||
Write-Log "Waiting 60 seconds for reboot ..."
|
||||
Start-Sleep -Seconds 60
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Install-RoleSecondaryDomainController
|
||||
{
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Install additional (secondary) domain controller.
|
||||
|
||||
#>
|
||||
param
|
||||
(
|
||||
[String]
|
||||
# Domain name to join to.
|
||||
$DomainName,
|
||||
|
||||
[String]
|
||||
# Domain user who is allowed to join computer to domain.
|
||||
$UserName,
|
||||
|
||||
[String]
|
||||
# User's password.
|
||||
$Password,
|
||||
|
||||
[String]
|
||||
# Domain controller recovery mode password.
|
||||
$SafeModePassword
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
$Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password
|
||||
|
||||
# Add required windows features
|
||||
Add-WindowsFeatureWrapper `
|
||||
-Name "DNS","AD-Domain-Services","RSAT-DFS-Mgmt-Con" `
|
||||
-IncludeManagementTools `
|
||||
-NotifyRestart
|
||||
|
||||
|
||||
Write-Log "Adding secondary domain controller ..."
|
||||
|
||||
$SMAP = ConvertTo-SecureString -String $SafeModePassword -AsPlainText -Force
|
||||
|
||||
Install-ADDSDomainController `
|
||||
-DomainName $DomainName `
|
||||
-SafeModeAdministratorPassword $SMAP `
|
||||
-Credential $Credential `
|
||||
-NoRebootOnCompletion `
|
||||
-Force `
|
||||
-ErrorAction Stop | Out-Null
|
||||
|
||||
Write-Log "Waiting for restart ..."
|
||||
# Stop-Execution -ExitCode 3010 -ExitString "Computer must be restarted to finish domain controller promotion."
|
||||
# Write-Log "Restarting computer ..."
|
||||
# Restart-Computer -Force
|
||||
}
|
||||
}
|
84
data/templates/agent/scripts/Install-SQLServer.ps1
Normal file
84
data/templates/agent/scripts/Install-SQLServer.ps1
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
Function ConvertTo-Boolean {
|
||||
param (
|
||||
$InputObject,
|
||||
[Boolean] $Default = $false
|
||||
)
|
||||
try {
|
||||
[System.Convert]::ToBoolean($InputObject)
|
||||
}
|
||||
catch {
|
||||
$Default
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Function Show-Environment {
|
||||
foreach ($item in (Get-ChildItem Env:)) {
|
||||
Write-Log ("'{0}' --> '{1}'" -f $item.Name, $item.Value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Function Install-SqlServer {
|
||||
param (
|
||||
[String] $SetupRoot = '',
|
||||
[String] $SAPassword = '',
|
||||
[String] $MuranoFileShare = '',
|
||||
[Switch] $MixedModeAuth = $false,
|
||||
[Switch] $UpdateEnabled = $false
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($SetupRoot -eq '') {
|
||||
if ($MuranoFileShare -eq '') {
|
||||
$MuranoFileShare = [Environment]::GetEnvironmentVariable('MuranoFileShare')
|
||||
if ($MuranoFileShare -eq '') {
|
||||
throw("Unable to find MuranoFileShare path.")
|
||||
}
|
||||
}
|
||||
|
||||
$SetupRoot = [IO.Path]::Combine($MuranoFileShare, 'Prerequisites\SQL Server\2012')
|
||||
}
|
||||
|
||||
#$MixedModeAuthSwitch = ConvertTo-Boolean $MixedModeAuth
|
||||
|
||||
$ExtraOptions = @{}
|
||||
|
||||
if ($MixedModeAuth -eq $true) {
|
||||
$ExtraOptions += @{'SECURITYMODE' = 'SQL'}
|
||||
if ($SAPassword -eq '') {
|
||||
throw("SAPassword must be set when MixedModeAuth is requisted!")
|
||||
}
|
||||
}
|
||||
|
||||
if ($SAPassword -ne '') {
|
||||
$ExtraOptions += @{'SAPWD' = $SAPassword}
|
||||
}
|
||||
|
||||
if (-not $UpdateEnabled) {
|
||||
$ExtraOptions += @{'UpdateEnabled' = $false}
|
||||
}
|
||||
|
||||
Show-Environment
|
||||
|
||||
New-SqlServer -SetupRoot $SetupRoot -ExtraOptions $ExtraOptions
|
||||
}
|
||||
}
|
72
data/templates/agent/scripts/InstallIIS.ps1
Normal file
72
data/templates/agent/scripts/InstallIIS.ps1
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Install-WebServer {
|
||||
param (
|
||||
[String] $PrerequisitesPath
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "--> Install-WebServer"
|
||||
|
||||
$FeatureList = @(
|
||||
'Web-Server',
|
||||
'Web-Net-Ext45',
|
||||
'Web-ASP',
|
||||
'Web-Asp-Net45',
|
||||
'Web-ISAPI-Ext',
|
||||
'Web-ISAPI-Filter',
|
||||
'Web-Includes'
|
||||
)
|
||||
|
||||
$PrerequisitesList = @(
|
||||
'AspNetMvc4Setup.exe',
|
||||
'WebApplications.exe'
|
||||
)
|
||||
|
||||
$PrerequisitesPath = [IO.Path]::Combine($PrerequisitesPath, 'IIS')
|
||||
|
||||
Write-Log "Validating prerequisites based on the list ..."
|
||||
foreach ($FileName in $PrerequisitesList) {
|
||||
$FilePath = [IO.Path]::Combine($PrerequisitesPath, $FileName)
|
||||
if (-not (Test-Path -Path $FilePath -PathType Leaf)) {
|
||||
throw("Prerequisite file not found: '$FilePath'")
|
||||
}
|
||||
}
|
||||
|
||||
Import-Module ServerManager
|
||||
|
||||
Write-Log "Installing Web Server ..."
|
||||
Install-WindowsFeature $FeatureList -IncludeManagementTools
|
||||
|
||||
Write-Log "Installing AspNetMvp4 ..."
|
||||
$Exec = Exec -FilePath $([IO.Path]::Combine($PrerequisitesPath, 'AspNetMvc4Setup.exe')) -ArgumentList '/q' -PassThru
|
||||
if ($Exec.ExitCode -ne 0) {
|
||||
throw("Installation of 'AspNetMvc4Setup.exe' failed. Process exit code '$($Exec.ExitCode)'")
|
||||
}
|
||||
|
||||
# Extract WebApplications folder with *.target files to
|
||||
# C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0
|
||||
Write-Log "Installing WebApplication targets ..."
|
||||
$WebApplicationsTargetsRoot = 'C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0'
|
||||
$null = New-Item -Path $WebApplicationsTargetsRoot -ItemType Container
|
||||
$Exec = Exec -FilePath $([IO.Path]::Combine($PrerequisitesPath, 'WebApplications.exe')) -ArgumentList @("-o`"$WebApplicationsTargetsRoot`"", '-y') -PassThru
|
||||
if ($Exec.ExitCode -ne 0) {
|
||||
throw("Installation of 'WebApplications.exe' failed. Process exit code '$($Exec.ExitCode)'")
|
||||
}
|
||||
|
||||
Write-Log "<-- Install-WebServer"
|
||||
}
|
||||
}
|
65
data/templates/agent/scripts/Join-Domain.ps1
Normal file
65
data/templates/agent/scripts/Join-Domain.ps1
Normal file
@ -0,0 +1,65 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Join-Domain {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Executes "Join domain" action.
|
||||
|
||||
Requires 'CoreFunctions' module.
|
||||
#>
|
||||
param (
|
||||
[String] $DomainName = '',
|
||||
[String] $UserName = '',
|
||||
[String] $Password = '',
|
||||
[String] $OUPath = '',
|
||||
[Switch] $AllowRestart
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($UserName -eq '') {
|
||||
$UserName = 'Administrator'
|
||||
}
|
||||
|
||||
$Credential = New-Credential -UserName "$DomainName\$UserName" -Password $Password
|
||||
|
||||
|
||||
if (Test-ComputerName -DomainName $DomainName -ErrorAction 'SilentlyContinue') {
|
||||
Write-LogWarning "Computer already joined to domain '$DomainName'"
|
||||
}
|
||||
else {
|
||||
Write-Log "Joining computer to domain '$DomainName' ..."
|
||||
|
||||
if ($OUPath -eq '') {
|
||||
Add-Computer -DomainName $DomainName -Credential $Credential -Force
|
||||
}
|
||||
else {
|
||||
Add-Computer -DomainName $DomainName -Credential $Credential -OUPath $OUPath -Force
|
||||
}
|
||||
|
||||
Write-Log "Waiting 30 seconds to restart ..."
|
||||
Start-Sleep -Seconds 30
|
||||
<#
|
||||
if ($AllowRestart) {
|
||||
Write-Log "Restarting computer ..."
|
||||
Restart-Computer -Force
|
||||
}
|
||||
else {
|
||||
Write-Log "Please restart the computer now."
|
||||
}
|
||||
#>
|
||||
}
|
||||
}
|
||||
}
|
64
data/templates/agent/scripts/New-SqlServerSystemAccount.ps1
Normal file
64
data/templates/agent/scripts/New-SqlServerSystemAccount.ps1
Normal file
@ -0,0 +1,64 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
function New-SqlServerSystemAccount {
|
||||
param (
|
||||
# (REQUIRED) Domain Name
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $DomainName,
|
||||
|
||||
# (REQUIRED) User name who has permissions to create and modify userPassword
|
||||
# Usually this is the domain administrator '$domainName\Administrator' account
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $UserName,
|
||||
|
||||
# (REQUIRED) Password for that user
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $UserPassword,
|
||||
|
||||
# (REQUIRED) User name for a new account that will be used to run SQL Server
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $SQLServiceUserName,
|
||||
|
||||
# (REQUIRED) Password for that user
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $SQLServiceUserPassword,
|
||||
|
||||
[String] $PrimaryNode = ' '
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($PrimaryNode.ToLower() -ne ($Env:ComputerName).ToLower()) {
|
||||
Write-Log "THis function runs on AOAG primary node only."
|
||||
Write-Log "Exiting."
|
||||
return
|
||||
}
|
||||
|
||||
Write-Log "Installing 'RSAT-AD-PowerShell' ... "
|
||||
Add-WindowsFeature RSAT-AD-PowerShell
|
||||
|
||||
Import-Module ActiveDirectory
|
||||
|
||||
$Creds = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
|
||||
Write-Log "Adding new user ..."
|
||||
$null = New-ADUser `
|
||||
-Name $SQLServiceUserName `
|
||||
-AccountPassword $(ConvertTo-SecureString -String $SQLServiceUserPassword -AsPlainText -Force) `
|
||||
-Credential $Creds `
|
||||
-ErrorAction 'Stop'
|
||||
}
|
||||
}
|
280
data/templates/agent/scripts/OptionParser.ps1
Normal file
280
data/templates/agent/scripts/OptionParser.ps1
Normal file
@ -0,0 +1,280 @@
|
||||
function New-Option ([string]$Name, [switch]$Switch, [switch]$Boolean, [switch]$String, [switch]$List, $Constraints=$null) {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates Option object
|
||||
|
||||
.DESCRIPTION
|
||||
Option object is a virtual object represtnting typed command line option. These objects encapsulate escaping and
|
||||
validation matters.
|
||||
|
||||
One and only one of the switches 'Switch', 'Boolean', 'String' or 'List' should be provided.
|
||||
|
||||
.PARAMETER Name
|
||||
Option name as it appears in the command line.
|
||||
|
||||
.PARAMETER Switch
|
||||
Use this switch to create valueless option (a switch).
|
||||
|
||||
.PARAMETER Boolean
|
||||
Use this switch to create boolean option. Its value is always converted to "1" or "0"
|
||||
|
||||
.PARAMETER String
|
||||
Use this switch to create string option. Its value will be properly quoted if necessary.
|
||||
|
||||
.PARAMETER List
|
||||
Use this switch to create option with list value. Values will be put into command line using valid value delemiter (a comma)
|
||||
|
||||
.PARAMETER Constraints
|
||||
When this parameter is specified, option values are limited to options from that list.
|
||||
|
||||
#>
|
||||
|
||||
$Option = New-Object -TypeName PSObject
|
||||
|
||||
# Fields
|
||||
$Option | Add-Member NoteProperty Type -value $null
|
||||
$Option | Add-Member NoteProperty Name -value $null
|
||||
$Option | Add-Member NoteProperty AllowedValues -value $null
|
||||
|
||||
# Init
|
||||
|
||||
$Option | Add-Member ScriptMethod __init__ {
|
||||
param([string]$Name, $Switch, $Boolean, $String, $List)
|
||||
|
||||
$this.Name = $Name
|
||||
|
||||
# With respect for our developers we do not check for double type selected
|
||||
if ($Switch) {
|
||||
AugmentOptionSwitch($this)
|
||||
} elseif ($Boolean) {
|
||||
AugmentOptionBoolean($this)
|
||||
} elseif ($String) {
|
||||
AugmentOptionString($this)
|
||||
} elseif ($List) {
|
||||
AugmentOptionList($this)
|
||||
} else {
|
||||
throw "Switch, Boolean, String or List option type must be provided for option '$Name'"
|
||||
}
|
||||
}
|
||||
|
||||
$Option | Add-Member ScriptMethod __post_init__ {
|
||||
param($Constraints=$null)
|
||||
if ($Constraints -ne $null) {
|
||||
$this.AllowedValues = @()
|
||||
$this.AllowedValues = $this.AllowedValues + $Constraints
|
||||
} else {
|
||||
$Constraints = $null
|
||||
}
|
||||
}
|
||||
|
||||
# Methods
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod Validate {
|
||||
if ($this.AllowedValues -ne $null) {
|
||||
if (-not($this.AllowedValues -contains $this.Value)) {
|
||||
$Cts = $this.AllowedValues -join ','
|
||||
throw "Option '$($this.Name)' may have values ($Cts) but not '$($this.Value)'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod ToString {
|
||||
return "/$($this.Name)"
|
||||
}
|
||||
|
||||
# invoke constructor
|
||||
|
||||
$Option.__init__($Name, $Switch, $Boolean, $String, $List)
|
||||
$Option.__post_init__($Constraints)
|
||||
|
||||
return $Option
|
||||
}
|
||||
|
||||
function AugmentOptionSwitch($Option) {
|
||||
}
|
||||
|
||||
function AugmentOptionBoolean($Option) {
|
||||
# Fields
|
||||
$Option | Add-Member NoteProperty Value -value $false
|
||||
|
||||
# Methods
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod ToString {
|
||||
if ($this.Value) {
|
||||
return "/$($this.Name)=1"
|
||||
} else {
|
||||
return "/$($this.Name)=0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AugmentOptionString($Option) {
|
||||
# Fields
|
||||
$Option | Add-Member NoteProperty Value -value ""
|
||||
|
||||
# Methods
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod ToString {
|
||||
$v = "$($this.Value)"
|
||||
if ($v -match '.* .*') {
|
||||
# TODO: Escape double quote characters if possible
|
||||
return "/$($this.Name)=`"$v`""
|
||||
} else {
|
||||
return "/$($this.Name)=$v"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AugmentOptionList($Option) {
|
||||
# Fields
|
||||
$Option | Add-Member NoteProperty Value -value @()
|
||||
|
||||
# Methods
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod Validate {
|
||||
if ($this.AllowedValues -ne $null) {
|
||||
foreach ($V in $this.Value) {
|
||||
if (-not($this.AllowedValues -contains $V)) {
|
||||
$Cts = $this.AllowedValues -join ','
|
||||
throw "Option '$($this.Name)' may have values ($Cts) but not '$V'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Option | Add-Member -Force ScriptMethod ToString {
|
||||
return "/$($this.Name)=$($this.Value -join ',')"
|
||||
}
|
||||
}
|
||||
|
||||
function New-OptionParser() {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates OptionParser object.
|
||||
|
||||
.DESCRIPTION
|
||||
OptionParser object leverages Option objects capabilities and builds valid command line using specified options.
|
||||
An application may also be invoked with OptionParser.
|
||||
|
||||
#>
|
||||
|
||||
$OptionParser = New-Object -TypeName PSObject
|
||||
|
||||
# Fields
|
||||
$OptionParser | Add-Member NoteProperty Options -value @{}
|
||||
$OptionParser | Add-Member NoteProperty Defaults -value @{}
|
||||
$OptionParser | Add-Member NoteProperty RequiredOptions -value @()
|
||||
|
||||
# Methods
|
||||
|
||||
$OptionParser | Add-Member ScriptMethod AddOption {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Adds supported option into OptionParser.
|
||||
|
||||
.DESCRIPTION
|
||||
OptionParser does not allow using unrecognized options. Use this method to fill OptionParser with recognized options
|
||||
|
||||
.PARAMETER Option
|
||||
Option object
|
||||
|
||||
.PARAMETER Required
|
||||
Required option switch
|
||||
|
||||
.PARAMETER Default
|
||||
Option default value
|
||||
#>
|
||||
param($Option, [bool]$Required=$false, $Default=$null)
|
||||
$this.Options.Add($Option.Name, $Option)
|
||||
if ($Required) {
|
||||
$this.RequiredOptions = $this.RequiredOptions + $Option.Name
|
||||
if ($Option | Get-Member "Value") {
|
||||
if ($Default) {
|
||||
$this.Defaults.Add($Option.Name, $Default)
|
||||
}
|
||||
} else {
|
||||
$this.Defaults.Add($Option.Name, $null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$OptionParser | Add-Member ScriptMethod Parse {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Parses supplied options and returns command line parameters array.
|
||||
|
||||
.DESCRIPTION
|
||||
This method verifies that only supported options are provided, all mandatory options are in place,
|
||||
all option meet constraints if any. Unspecified options with default values are added to command line.
|
||||
So, mandatory option with default value never causes exception.
|
||||
|
||||
.PARAMETER Options
|
||||
A hash map of options to parse. Option names should be mapped to corresponding values.
|
||||
#>
|
||||
param([hashtable]$Options)
|
||||
|
||||
$CommandLine = @()
|
||||
foreach ($RequiredOptionName in $this.RequiredOptions) {
|
||||
if (-not $Options.ContainsKey($RequiredOptionName)) {
|
||||
$Default = $this.Defaults.Get_Item($RequiredOptionName)
|
||||
if ($this.Defaults.ContainsKey($RequiredOptionName)) {
|
||||
$Options.Add($RequiredOptionName, $this.Defaults.Get_Item($RequiredOptionName))
|
||||
} else {
|
||||
throw "Required option '$RequiredOptionName' is missing"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($OptionName in $($Options.keys)) {
|
||||
$Option = $this.Options.Get_Item($OptionName)
|
||||
if ($Option -eq $null) {
|
||||
throw "Option '$OptionName' is not allowed"
|
||||
}
|
||||
if ($Option | Get-Member "Value") {
|
||||
$Option.Value = $Options.Get_Item($OptionName)
|
||||
}
|
||||
$Option.Validate()
|
||||
$CommandLine = $CommandLine + $Option.ToString()
|
||||
}
|
||||
return $CommandLine
|
||||
}
|
||||
|
||||
$OptionParser | Add-Member ScriptMethod ExecuteBinary {
|
||||
param($Binary, [hashtable]$Options = @{}, $CommandLineSuffix = @())
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Executes binary with a command line constructed from provided options. An arbitrary suffix may be
|
||||
appended to the command line.
|
||||
|
||||
.DESCRIPTION
|
||||
This method uses OptionParser.Parse method to construct command line. If there a command line suffix
|
||||
was supplied, it is appended to the end of command line. Normally command line suffix should contain
|
||||
leading space character.
|
||||
|
||||
Method waits for executable process to complete and returns its exit code.
|
||||
|
||||
.PARAMETER Binary
|
||||
Full or relative path to the executable to run.
|
||||
|
||||
.PARAMETER Options
|
||||
A hash map of options to pass to the executable.
|
||||
|
||||
.PARAMETER CommandLineSuffix
|
||||
Arbitrary command line suffix. Normally it shoud have leading space character.
|
||||
#>
|
||||
|
||||
$Binary = Get-Item $Binary
|
||||
$CommandLine = $this.Parse($Options)
|
||||
if ($CommandLineSuffix) {
|
||||
$CommandLine = $CommandLine + $CommandLineSuffix
|
||||
}
|
||||
|
||||
Write-Log "Executing: $($Binary.FullName) $($CommandLine -join ' ')"
|
||||
$process = [System.Diagnostics.Process]::Start($Binary, $CommandLine)
|
||||
$process.WaitForExit()
|
||||
$process.Refresh()
|
||||
return $process.ExitCode
|
||||
}
|
||||
|
||||
return $OptionParser
|
||||
}
|
538
data/templates/agent/scripts/SQLServerForAOAG.ps1
Normal file
538
data/templates/agent/scripts/SQLServerForAOAG.ps1
Normal file
@ -0,0 +1,538 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
function Install-SqlServerPowerShellModule {
|
||||
param (
|
||||
[String] $SetupRoot = ''
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ((Get-Module SQLPS -ListAvailable) -ne $null) {
|
||||
Write-Log "Module SQLSP already installed."
|
||||
return
|
||||
}
|
||||
|
||||
if ($MuranoFileShare -eq '') {
|
||||
$MuranoFileShare = [String]([Environment]::GetEnvironmentVariable('MuranoFileShare'))
|
||||
if ($MuranoFileShare -eq '') {
|
||||
throw "Unable to find MuranoFileShare path."
|
||||
}
|
||||
}
|
||||
Write-LogDebug "MuranoFileShare = '$MuranoFileShare'"
|
||||
|
||||
if ($SetupRoot -eq '') {
|
||||
$SetupRoot = [IO.Path]::Combine("$MuranoFileShare", 'Prerequisites\SQL Server\Tools')
|
||||
}
|
||||
Write-LogDebug "SetupRoot = '$SetupRoot'"
|
||||
|
||||
$FileList = @(
|
||||
'SQLSysClrTypes.msi',
|
||||
'SharedManagementObjects.msi',
|
||||
'PowerShellTools.msi'
|
||||
)
|
||||
|
||||
foreach ($MsiFile in $FileList) {
|
||||
Write-Log "Trying to install '$MsiFile' ..."
|
||||
$MsiPath = Join-Path $SetupRoot $MsiFile
|
||||
if ([IO.File]::Exists($MsiPath)) {
|
||||
Write-Log "Starting msiexe ..."
|
||||
$Result = Exec -FilePath "msiexec.exe" -ArgumentList @('/i', "`"$MsiPath`"", '/quiet') -PassThru
|
||||
if ($Result.ExitCode -ne 0) {
|
||||
throw "Installation of MSI package '$MsiPath' failed with error code '$($Result.ExitCode)'"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Log "File '$MsiPath' not found."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Install-SqlServerForAOAG {
|
||||
param (
|
||||
# Path to folder where msi files for additional SQL features are located
|
||||
[String] $SetupRoot = '',
|
||||
|
||||
# Path to folder where msi files for additional SQLPS module are located
|
||||
[String] $SqlpsSetupRoot = '',
|
||||
|
||||
[String] $MuranoFileShare = '',
|
||||
|
||||
# (REQUIRED) Domain name
|
||||
[String] $SQLServiceUserDomain = 'fc-acme.local',
|
||||
|
||||
# (REQUIRED) User name for the account which will be used by SQL service
|
||||
[String] $SQLServiceUserName = 'Administrator',
|
||||
|
||||
# (REQUIRED) Password for that user
|
||||
[String] $SQLServiceUserPassword = 'P@ssw0rd',
|
||||
|
||||
[Switch] $UpdateEnabled
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($MuranoFileShare -eq '') {
|
||||
$MuranoFileShare = [String]([Environment]::GetEnvironmentVariable('MuranoFileShare'))
|
||||
if ($MuranoFileShare -eq '') {
|
||||
throw "Unable to find MuranoFileShare path."
|
||||
}
|
||||
}
|
||||
Write-LogDebug "MuranoFileShare = '$MuranoFileShare'"
|
||||
|
||||
if ($SetupRoot -eq '') {
|
||||
$SetupRoot = [IO.Path]::Combine("$MuranoFileShare", 'Prerequisites\SQL Server\2012')
|
||||
}
|
||||
Write-LogDebug "SetupRoot = '$SetupRoot'"
|
||||
|
||||
$ExtraOptions = @{}
|
||||
|
||||
if ($UpdateEnabled) {
|
||||
$ExtraOptions += @{'UpdateEnabled' = $true}
|
||||
}
|
||||
else {
|
||||
$ExtraOptions += @{'UpdateEnabled' = $false}
|
||||
}
|
||||
|
||||
$null = New-SQLServerForAOAG `
|
||||
-SetupRoot $SetupRoot `
|
||||
-SQLSvcUsrDomain $SQLServiceUserDomain `
|
||||
-SQLSvcUsrName $SQLServiceUserName `
|
||||
-SQLSvcUsrPassword $SQLServiceUserPassword `
|
||||
-ExtraOptions $ExtraOptions
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Initialize-AlwaysOnAvailabilityGroup {
|
||||
param (
|
||||
[String] $DomainName,
|
||||
[String] $DomainAdminAccountName,
|
||||
[String] $DomainAdminAccountPassword,
|
||||
[String] $SqlServiceAccountName,
|
||||
[String] $PrimaryNode,
|
||||
[String] $ShareName = 'SharedWorkDir'
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
$ShareNetworkPath = '\\' + $PrimaryNode + '\' + $ShareName
|
||||
|
||||
$DomainAdminAccountCreds = New-Credential `
|
||||
-UserName "$DomainName\$DomainAdminAccountName" `
|
||||
-Password "$DomainAdminAccountPassword"
|
||||
|
||||
$FunctionsFile = Export-Function 'Get-NextFreePort', 'Initialize-AlwaysOn'
|
||||
|
||||
$null = Start-PowerShellProcess @"
|
||||
trap {
|
||||
`$_
|
||||
exit 1
|
||||
}
|
||||
|
||||
Import-Module CoreFunctions
|
||||
|
||||
Write-Log "Importing functions file '$FunctionsFile' ..."
|
||||
. "$FunctionsFile"
|
||||
|
||||
Write-Log "Starting 'Initialize-AlwaysOn' ..."
|
||||
`$XmlFile = [IO.Path]::Combine("$ShareNetworkPath", "`$(`$Env:ComputerName).xml")
|
||||
Write-Log "Output XML file is '`$XmlFile'"
|
||||
Initialize-AlwaysOn | Export-CliXml -Path `$XmlFile
|
||||
"@ -Credential $DomainAdminAccountCreds -NoBase64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function New-SharedFolderForAOAG {
|
||||
param (
|
||||
# (OPTIONAL)
|
||||
[String] $SharePath = [IO.Path]::Combine($Env:SystemDrive + '\', 'SharedWorkDir'),
|
||||
|
||||
# (OPTIONAL)
|
||||
[String] $ShareName = 'SharedWorkDir',
|
||||
|
||||
[String] $PrimaryNode = ' '
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($PrimaryNode.ToLower() -ne ($Env:ComputerName).ToLower()) {
|
||||
Write-Log "This script runs on primary node only."
|
||||
Write-Log "Exiting script."
|
||||
return
|
||||
}
|
||||
|
||||
if ($ShareName -eq '') {
|
||||
$ShareName = [IO.Path]::GetFileNameWithoutExtension($SharePath)
|
||||
}
|
||||
|
||||
Write-LogDebug "SharePath = '$SharePath'"
|
||||
Write-LogDebug "ShareName = '$ShareName'"
|
||||
|
||||
try {
|
||||
Write-LogDebug "Trying to remove share '$ShareName'"
|
||||
$null = Get-SmbShare -Name $ShareName -ErrorAction 'Stop'
|
||||
$null = Remove-SmbShare -Name $ShareName -Force
|
||||
write-Log "Share '$ShareName' removed."
|
||||
}
|
||||
catch {
|
||||
Write-LogWarning "Share '$ShareName' not exists or cannot be deleted."
|
||||
}
|
||||
|
||||
try {
|
||||
Write-LogDebug "Trying to remove folder '$SharePath"
|
||||
$null = Get-Item -Path $SharePath -ErrorAction 'Stop'
|
||||
$null = Remove-Item -Path $SharePath -Recurse -Force
|
||||
Write-Log "Folder '$SharePath' removed."
|
||||
}
|
||||
catch {
|
||||
Write-LogWarning "Folder '$SharePath' not exists or cannot be deleted."
|
||||
}
|
||||
|
||||
$null = New-Item -Path $SharePath -ItemType Container -Force
|
||||
|
||||
$null = New-SmbShare -Path $SharePath `
|
||||
-Name $ShareName `
|
||||
-FullAccess "Everyone" `
|
||||
-Description "Shared folder for AlwaysOn Availability Group setup."
|
||||
|
||||
return '\\' + $Env:ComputerName + '\' + $ShareName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function New-DatabaseForAOAG {
|
||||
param (
|
||||
[String] $DatabaseName,
|
||||
[String] $DomainName,
|
||||
[String] $UserName,
|
||||
[String] $UserPassword
|
||||
)
|
||||
|
||||
$Creds = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
|
||||
$FunctionsFile = Export-Function 'Invoke-SQLText', 'ConvertTo-SQLName', 'ConvertTo-SQLString', 'New-SQLDatabase'
|
||||
|
||||
Start-PowerShellProcess @"
|
||||
trap {
|
||||
`$_
|
||||
exit 1
|
||||
}
|
||||
|
||||
Import-Module CoreFunctions
|
||||
|
||||
Write-Log "Importing functions from file '$FunctionsFile' ..."
|
||||
. "$FunctionsFile"
|
||||
|
||||
Write-Log "Starting 'New-SQLDatabase' ..."
|
||||
New-SQLDatabase $DatabaseName
|
||||
"@ -Credential $Creds -NoBase64
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Initialize-AOAGPrimaryReplica {
|
||||
param (
|
||||
# (OPTIONAL) Name of the new Availability Group. If not specified then default name will be used.
|
||||
[String] $GroupName = 'MuranoAG',
|
||||
|
||||
# (REQUIRED) Nodes that will be configured as replica partners.
|
||||
#[Parameter(Mandatory=$true)]
|
||||
[String[]] $NodeList,
|
||||
|
||||
# (REQUIRED) Node name that will be primary for selected Availability Group
|
||||
#[Parameter(Mandatory=$true)]
|
||||
[String] $PrimaryNode,
|
||||
|
||||
# (REQUIRED) Database list that will be added to the Availability Group
|
||||
#[Parameter(Mandatory=$true)]
|
||||
[String[]] $DatabaseList,
|
||||
|
||||
# (REQUIRED) Listener name that will be used by clients to connect to databases in that AG
|
||||
#[Parameter(Mandatory=$true)]
|
||||
[String] $ListenerName = 'MuranoAG_Listener',
|
||||
|
||||
# (REQUIRED) IP address of the listener
|
||||
#[Parameter(Mandatory=$true)]
|
||||
[String] $ListenerIP,
|
||||
|
||||
[String] $ListenerIPMask = '255.255.255.0',
|
||||
|
||||
[String] $ListenerPort = '5023',
|
||||
|
||||
# Sync Mode Node List
|
||||
[String[]] $SyncModeNodeList,
|
||||
|
||||
[String] $SharedWorkDir = 'SharedWorkDir',
|
||||
|
||||
[String] $CliXmlFile = '',
|
||||
|
||||
[String] $DomainName,
|
||||
[String] $UserName,
|
||||
[String] $UserPassword
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Write-Log "Primary node: '$($PrimaryNode.ToLower())'"
|
||||
Write-Log "Current node: '$(($Env:ComputerName).ToLower())'"
|
||||
|
||||
if ($PrimaryNode.ToLower() -ne $($Env:ComputerName).ToLower()) {
|
||||
Write-Log "This function works on PrimaryNode only."
|
||||
Write-Log "Exiting."
|
||||
return
|
||||
}
|
||||
|
||||
if ($CliXmlFile -eq '') {
|
||||
$ReplicaDefinitionList = @()
|
||||
foreach ($Node in $NodeList) {
|
||||
try {
|
||||
$NodeEndpointPort = Import-CliXml -Path "\\$PrimaryNode\SharedWorkDir\$Node.xml"
|
||||
}
|
||||
catch {
|
||||
Write-Log "Using default endpoint port 5022"
|
||||
$NodeEndpointPort = 5022
|
||||
}
|
||||
|
||||
$ReplicaDefinition = @{
|
||||
"SERVER_INSTANCE" = "$Node";
|
||||
"ENDPOINT_URL" = "TCP://${Node}:${NodeEndpointPort}";
|
||||
"AVAILABILITY_MODE" = "ASYNCHRONOUS_COMMIT";
|
||||
"FAILOVER_MODE"="MANUAL";
|
||||
}
|
||||
|
||||
if ($SyncModeNodeList -contains $Node) {
|
||||
Write-Log "$Node is in SyncModeNodeList"
|
||||
$ReplicaDefinition['AVAILABILITY_MODE'] = "SYNCHRONOUS_COMMIT"
|
||||
$ReplicaDefinition['FAILOVER_MODE'] = "AUTOMATIC"
|
||||
}
|
||||
else {
|
||||
Write-Log "$Node is NOT in SyncModeNodeList"
|
||||
}
|
||||
|
||||
$ReplicaDefinitionList += @($ReplicaDefinition)
|
||||
}
|
||||
|
||||
$Preferences = @{}
|
||||
|
||||
$ListenerDefinition = @{
|
||||
"NAME"=$ListenerName;
|
||||
"PORT" = "$ListenerPort";
|
||||
"STATIC" = "$ListenerIP/$ListenerIPMask"
|
||||
}
|
||||
|
||||
$Parameters = @{
|
||||
'WorkDir' = "\\$PrimaryNode\$SharedWorkDir";
|
||||
'Name' = $GroupName;
|
||||
'DatabaseNames' = $DatabaseList;
|
||||
'ReplicaDefs' = $ReplicaDefinitionList;
|
||||
'Preferences' = $Preferences;
|
||||
'ListenerDef' = $ListenerDefinition;
|
||||
}
|
||||
|
||||
$null = Remove-Item -Path "\\$PrimaryNode\SharedWorkDir\*" -Force
|
||||
|
||||
$CliXmlFile = [IO.Path]::GetTempFileName()
|
||||
|
||||
Write-LogDebug "CliXml file: '$CliXmlFile'"
|
||||
|
||||
$null = Export-CliXml -Path $CliXmlFile -InputObject $Parameters -Depth 10
|
||||
|
||||
$null = Initialize-AOAGPrimaryReplica `
|
||||
-CliXmlFile $CliXmlFile `
|
||||
-DomainName $DomainName `
|
||||
-UserName $UserName `
|
||||
-UserPassword $UserPassword `
|
||||
-PrimaryNode $PrimaryNode
|
||||
|
||||
Write-LogDebug "Inner 'Initialize-AOAGPrimaryReplica' call completed."
|
||||
}
|
||||
else {
|
||||
$Creds = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
|
||||
$FunctionsFile = Export-Function -All
|
||||
|
||||
$null = Start-PowerShellProcess @"
|
||||
trap {
|
||||
`$_
|
||||
exit 1
|
||||
}
|
||||
|
||||
Import-Module CoreFunctions
|
||||
|
||||
Write-Log "Importing functions from '$FunctionsFile' ..."
|
||||
. "$FunctionsFile"
|
||||
|
||||
Write-Log "Importing CliXml parameters file ..."
|
||||
`$Parameters = Import-CliXml -Path $CliXmlFile
|
||||
|
||||
Write-Log "Starting 'New-AlwaysOnAvailabilityGroup' ..."
|
||||
New-AlwaysOnAvailabilityGroup ``
|
||||
-WorkDir `$Parameters['WorkDir'] ``
|
||||
-Name `$Parameters['Name'] ``
|
||||
-DatabaseNames `$Parameters['DatabaseNames'] ``
|
||||
-ReplicaDefs `$Parameters['ReplicaDefs'] ``
|
||||
-Preferences `$Parameters['Preferences'] ``
|
||||
-ListenerDef `$Parameters['ListenerDef']
|
||||
"@ -Credential $Creds -NoBase64
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Initialize-AOAGSecondaryReplica {
|
||||
param (
|
||||
# (REQUIRED) Nodes that will be configured as replica partners.
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String[]] $NodeList,
|
||||
|
||||
# (REQUIRED) Node name that will be primary for selected Availability Group
|
||||
[Parameter(Mandatory=$true)]
|
||||
[String] $PrimaryNode,
|
||||
|
||||
[String] $SharedWorkDir = 'SharedWorkDir',
|
||||
|
||||
[String] $DomainName,
|
||||
[String] $UserName,
|
||||
[String] $UserPassword
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ($PrimaryNode.ToLower() -eq ($Env:ComputerName).ToLower()) {
|
||||
Write-Log "This function works on any SecondaryNode only."
|
||||
Write-Log "Exiting."
|
||||
return
|
||||
}
|
||||
|
||||
$Creds = New-Credential -UserName "$DomainName\$UserName" -Password "$UserPassword"
|
||||
|
||||
$FunctionsFile = Export-Function -All
|
||||
|
||||
$null = Start-PowerShellProcess @"
|
||||
trap {
|
||||
$_
|
||||
exit 1
|
||||
}
|
||||
|
||||
Import-Module CoreFunctions
|
||||
|
||||
Write-Log "Importing functions from '$FunctionsFile' ..."
|
||||
. "$FunctionsFile"
|
||||
|
||||
Write-Log "Starting 'New-AlwaysOnAvailabilityGroupReplica' ..."
|
||||
New-AlwaysOnAvailabilityGroupReplica -WorkDir "\\$PrimaryNode\$SharedWorkDir"
|
||||
"@ -Credential $Creds -NoBase64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Disable-Firewall {
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
netsh advfirewall set allprofiles state off
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Enable-Firewall {
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
netsh advfirewall set allprofiles state on
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Enable-TrustedHosts {
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
Set-Item WSMan:\localhost\Client\TrustedHosts -Value '*' -Force
|
||||
}
|
||||
}
|
1373
data/templates/agent/scripts/SQLServerInstaller.ps1
Normal file
1373
data/templates/agent/scripts/SQLServerInstaller.ps1
Normal file
File diff suppressed because it is too large
Load Diff
367
data/templates/agent/scripts/SQLServerOptionParsers.ps1
Normal file
367
data/templates/agent/scripts/SQLServerOptionParsers.ps1
Normal file
@ -0,0 +1,367 @@
|
||||
function New-OptionParserInstall {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "INSTALL" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "INSTALL" action.
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$IsPartOfDomain = (Get-WmiObject Win32_ComputerSystem).PartOfDomain
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "INSTALL"), $true, "INSTALL")
|
||||
$OptionParser.AddOption((New-Option "IACCEPTSQLSERVERLICENSETERMS" -Switch), $true)
|
||||
$OptionParser.AddOption((New-Option "ENU" -Switch))
|
||||
#$OptionParser.AddOption((New-Option "UpdateEnabled" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UpdateEnabled" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "UpdateSource" -String))
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
$OptionParser.AddOption((New-Option "ERRORREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "FEATURES" -List -Constraints ("SQL","SQLEngine","Replication","FullText","DQ","AS","RS","DQC","IS","MDS","Tools","BC","BOL","BIDS","Conn","SSMS","ADV_SSMS","DREPLAY_CTLR","DREPLAY_CLT","SNAC_SDK","SDK","LocalDB")))
|
||||
$OptionParser.AddOption((New-Option "ROLE" -String -Constraints ("SPI_AS_ExistingFarm", "SPI_AS_NewFarm", "AllFeatures_WithDefaults")))
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSHAREDDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSHAREDWOWDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEID" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCENAME" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "PID" -String))
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "QS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UIMODE" -String -Constraints ("Normal", "AutoAdvance")))
|
||||
$OptionParser.AddOption((New-Option "SQMREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "AGTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "ASBACKUPDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "ASCOLLATION" -String))
|
||||
$OptionParser.AddOption((New-Option "ASCONFIGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "ASDATADIR" -String))
|
||||
$OptionParser.AddOption((New-Option "ASLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "ASSERVERMODE" -String -Constraints ("MULTIDIMENSIONAL", "POWERPIVOT", "TABULAR")))
|
||||
$OptionParser.AddOption((New-Option "ASSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "ASSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "ASSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
|
||||
#$OptionParser.AddOption((New-Option "ASSYSADMINACCOUNTS" -String), $true, "$ENV:USERDOMAIN\$ENV:USERNAME")
|
||||
if ($IsPartOfDomain) {
|
||||
$OptionParser.AddOption((New-Option "ASSYSADMINACCOUNTS" -String), $true, "$Env:USERDOMAIN\Administrator")
|
||||
}
|
||||
else {
|
||||
$OptionParser.AddOption((New-Option "ASSYSADMINACCOUNTS" -String), $true, "$Env:COMPUTERNAME\Administrator")
|
||||
}
|
||||
|
||||
$OptionParser.AddOption((New-Option "ASTEMPDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "ASPROVIDERMSOLAP" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "FARMACCOUNT" -String))
|
||||
$OptionParser.AddOption((New-Option "FARMPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "PASSPHRASE" -String))
|
||||
$OptionParser.AddOption((New-Option "FARMADMINIPORT" -String))
|
||||
$OptionParser.AddOption((New-Option "BROWSERSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "ENABLERANU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSQLDATADIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SAPWD" -String))
|
||||
$OptionParser.AddOption((New-Option "SECURITYMODE" -String -Constrainrs ("SQL")))
|
||||
$OptionParser.AddOption((New-Option "SQLBACKUPDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLCOLLATION" -String))
|
||||
$OptionParser.AddOption((New-Option "ADDCURRENTUSERASSQLADMIN" -Switch))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "SQLSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
|
||||
#$OptionParser.AddOption((New-Option "SQLSYSADMINACCOUNTS" -String), $true, "$ENV:USERDOMAIN\$ENV:USERNAME")
|
||||
if ($IsPartOfDomain) {
|
||||
$OptionParser.AddOption((New-Option "SQLSYSADMINACCOUNTS" -String), $true, "$ENV:USERDOMAIN\Administrator")
|
||||
}
|
||||
else {
|
||||
$OptionParser.AddOption((New-Option "SQLSYSADMINACCOUNTS" -String), $true, "$ENV:COMPUTERNAME\Administrator")
|
||||
}
|
||||
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMLEVEL" -String -Constraints ("0", "1", "2", "3")))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMSHARENAME" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCACCOUNT" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "ISSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "ISSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "ISSVCStartupType" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "NPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "TCPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "RSINSTALLMODE" -String -Constraints ("SharePointFilesOnlyMode", "DefaultNativeMode", "FilesOnlyMode")))
|
||||
$OptionParser.AddOption((New-Option "RSSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "RSSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "RSSVCStartupType" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserPrepareImage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "PrepareImage" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "PrepareImage" action.
|
||||
|
||||
Note that for installer version of MS SQL Server prior to 2012 SP1 Cumulative Update 2 only the
|
||||
following features are supported: SQLEngine, Replication, FullText, RS
|
||||
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "PrepareImage"), $true, "PrepareImage")
|
||||
$OptionParser.AddOption((New-Option "IACCEPTSQLSERVERLICENSETERMS" -Switch), $true)
|
||||
$OptionParser.AddOption((New-Option "ENU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UpdateEnabled" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UpdateSource" -String))
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
# $OptionParser.AddOption((New-Option "FEATURES" -List -Constraints ("SQLEngine","Replication","FullText","RS")))
|
||||
$OptionParser.AddOption((New-Option "FEATURES" -List -Constraints ("SQL","SQLEngine","Replication","FullText","DQ","AS","RS","DQC","IS","MDS","Tools","BC","BOL","BIDS","Conn","SSMS","ADV_SSMS","DREPLAY_CTLR","DREPLAY_CLT","SNAC_SDK","SDK","LocalDB")))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSHAREDDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEID" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "QS" -Switch))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserPrepareImageSP1U2 {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "PrepareImage" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "PrepareImage" action.
|
||||
|
||||
This cmdlet should be used only for MS SQL Server 2012 SP1 Cimilative Update 2 or later.
|
||||
|
||||
Note that for installer version of MS SQL Server prior to 2012 SP1 Cimilative Update 2 only the
|
||||
following features are supported: SQLEngine, Replication, FullText, RS
|
||||
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "PrepareImage"), $true, "PrepareImage")
|
||||
$OptionParser.AddOption((New-Option "IACCEPTSQLSERVERLICENSETERMS" -Switch), $true)
|
||||
$OptionParser.AddOption((New-Option "ENU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UpdateEnabled" -Switch))
|
||||
$OptionParser.AddOption((New-Option "UpdateSource" -String))
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
$OptionParser.AddOption((New-Option "FEATURES" -List -Constraints ("SQL","SQLEngine","Replication","FullText","DQ","AS","RS","DQC","IS","MDS","Tools","BC","BOL","BIDS","Conn","SSMS","ADV_SSMS","SNAC_SDK","SDK","LocalDB")))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSHAREDDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEID" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "QS" -Switch))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserCompleteImage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "CompleteImage" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "CompleteImage" action.
|
||||
|
||||
Note that INSTANCEID parameter value MUST be the same as specified on "PrepareImage" phase.
|
||||
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "CompleteImage"), $true, "CompleteImage")
|
||||
$OptionParser.AddOption((New-Option "IACCEPTSQLSERVERLICENSETERMS" -Switch), $true)
|
||||
$OptionParser.AddOption((New-Option "ENU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
$OptionParser.AddOption((New-Option "ERRORREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEID" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "INSTANCENAME" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "PID" -String))
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "QS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "SQMREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "AGTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "BROWSERSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "ENABLERANU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSQLDATADIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SAPWD" -String))
|
||||
$OptionParser.AddOption((New-Option "SECURITYMODE" -String -Constrainrs ("SQL")))
|
||||
$OptionParser.AddOption((New-Option "SQLBACKUPDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLCOLLATION" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "SQLSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "SQLSYSADMINACCOUNTS" -String), $true, "$ENV:USERDOMAIN\$ENV:USERNAME")
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMLEVEL" -String -Constraints ("0", "1", "2", "3")))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMSHARENAME" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCACCOUNT" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "NPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "TCPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "RSINSTALLMODE" -String -Constraints ("SharePointFilesOnlyMode", "DefaultNativeMode", "FilesOnlyMode")))
|
||||
$OptionParser.AddOption((New-Option "RSSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "RSSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "RSSVCStartupType" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserCompleteImageSP1U2 {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "CompleteImage" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "CompleteImage" action.
|
||||
|
||||
This cmdlet should be used only for MS SQL Server 2012 SP1 Cimilative Update 2 or later.
|
||||
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "CompleteImage"), $true, "CompleteImage")
|
||||
$OptionParser.AddOption((New-Option "IACCEPTSQLSERVERLICENSETERMS" -Switch), $true)
|
||||
$OptionParser.AddOption((New-Option "ENU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
$OptionParser.AddOption((New-Option "ERRORREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTANCEID" -String))
|
||||
$OptionParser.AddOption((New-Option "INSTANCENAME" -String))
|
||||
$OptionParser.AddOption((New-Option "PID" -String))
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "QS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "SQMREPORTING" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "AGTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "AGTSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "BROWSERSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "ENABLERANU" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTALLSQLDATADIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SAPWD" -String))
|
||||
$OptionParser.AddOption((New-Option "SECURITYMODE" -String -Constrainrs ("SQL")))
|
||||
$OptionParser.AddOption((New-Option "SQLBACKUPDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLCOLLATION" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "SQLSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLSVCSTARTUPTYPE" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
$OptionParser.AddOption((New-Option "SQLSYSADMINACCOUNTS" -String), $true, "$ENV:USERDOMAIN\$ENV:USERNAME")
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLTEMPDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "SQLUSERDBLOGDIR" -String))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMLEVEL" -String -Constraints ("0", "1", "2", "3")))
|
||||
$OptionParser.AddOption((New-Option "FILESTREAMSHARENAME" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCACCOUNT" -String))
|
||||
$OptionParser.AddOption((New-Option "FTSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "NPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "TCPENABLED" -Boolean))
|
||||
$OptionParser.AddOption((New-Option "RSINSTALLMODE" -String -Constraints ("SharePointFilesOnlyMode", "DefaultNativeMode", "FilesOnlyMode")))
|
||||
$OptionParser.AddOption((New-Option "RSSVCACCOUNT" -String), $true, "NT AUTHORITY\Network Service")
|
||||
$OptionParser.AddOption((New-Option "RSSVCPASSWORD" -String))
|
||||
$OptionParser.AddOption((New-Option "RSSVCStartupType" -String -Constraints ("Manual", "Automatic", "Disabled")))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserUpgrade {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserEditionUpgrade {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserRepair {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserRebuilddatabase {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserUninstall {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates an option parser for MS SQL Server 2012 setup "INSTALL" action.
|
||||
|
||||
.DESCRIPTION
|
||||
Use this cmdlet to create an option parser for MS SQL Server 2012 setup "INSTALL" action.
|
||||
All documented option are supported. See the following link for details:
|
||||
http://msdn.microsoft.com/en-us/library/ms144259.aspx
|
||||
#>
|
||||
$OptionParser = New-OptionParser
|
||||
|
||||
$OptionParser.AddOption((New-Option "ACTION" -String -Constraints "UNINSTALL"), $true, "UNINSTALL")
|
||||
$OptionParser.AddOption((New-Option "CONFIGURATIONFILE" -String))
|
||||
$OptionParser.AddOption((New-Option "FEATURES" -List -Constraints ("SQL","SQLEngine","Replication","FullText","DQ","AS","RS","DQC","IS","MDS","Tools","BC","BOL","BIDS","Conn","SSMS","ADV_SSMS","DREPLAY_CTLR","DREPLAY_CLT","SNAC_SDK","SDK","LocalDB")), $true)
|
||||
$OptionParser.AddOption((New-Option "INDICATEPROGRESS" -Switch))
|
||||
$OptionParser.AddOption((New-Option "INSTANCENAME" -String), $true, "MSSQLSERVER")
|
||||
$OptionParser.AddOption((New-Option "Q" -Switch))
|
||||
$OptionParser.AddOption((New-Option "HIDECONSOLE" -Switch))
|
||||
|
||||
return $OptionParser
|
||||
}
|
||||
|
||||
function New-OptionParserInstallFailoverCluster {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserPrepareFailoverCluster {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserCompleteFailoverCluster {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserUpgrade {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserAddNode {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
||||
|
||||
function New-OptionParserRemoveNode {
|
||||
# ToDo: Implement
|
||||
throw "Not yet implemented"
|
||||
}
|
37
data/templates/agent/scripts/SetLocalUserPassword.ps1
Normal file
37
data/templates/agent/scripts/SetLocalUserPassword.ps1
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
Function Set-LocalUserPassword {
|
||||
param (
|
||||
[String] $UserName,
|
||||
[String] $Password,
|
||||
[Switch] $Force
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
if ((Get-WmiObject Win32_UserAccount -Filter "LocalAccount = 'True' AND Name='$UserName'") -eq $null) {
|
||||
throw "Unable to find local user account '$UserName'"
|
||||
}
|
||||
|
||||
if ($Force) {
|
||||
Write-Log "Changing password for user '$UserName' to '*****'" # :)
|
||||
$null = ([ADSI] "WinNT://./$UserName").SetPassword($Password)
|
||||
}
|
||||
else {
|
||||
Write-LogWarning "You are trying to change password for user '$UserName'. To do this please run the command again with -Force parameter."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
151
data/templates/agent/scripts/Start-PowerShellProcess.ps1
Normal file
151
data/templates/agent/scripts/Start-PowerShellProcess.ps1
Normal file
@ -0,0 +1,151 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Select-CliXmlBlock {
|
||||
param (
|
||||
[String] $Path,
|
||||
[String] $OutFile = [IO.Path]::GetTempFileName()
|
||||
)
|
||||
|
||||
$TagFound = $false
|
||||
Get-Content $Path |
|
||||
ForEach-Object {
|
||||
if ($_ -eq '#< CLIXML') {
|
||||
$TagFound = $true
|
||||
}
|
||||
if ($TagFound) {
|
||||
Add-Content -Path $OutFile -Value $_
|
||||
}
|
||||
}
|
||||
$OutFile
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Start-PowerShellProcess {
|
||||
param (
|
||||
[String] $Command,
|
||||
$Credential = $null,
|
||||
[Switch] $IgnoreStdErr,
|
||||
[Switch] $NoBase64
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
$StdOut = [IO.Path]::GetTempFileName()
|
||||
$StdErr = [IO.Path]::GetTempFileName()
|
||||
|
||||
$ArgumentList = @('-OutputFormat', 'XML')
|
||||
|
||||
if ($NoBase64) {
|
||||
$TmpScript = [IO.Path]::GetTempFileName()
|
||||
Rename-Item -Path "$TmpScript" -NewName "$TmpScript.ps1" -Force
|
||||
$TmpScript = "$TmpScript.ps1"
|
||||
|
||||
Write-LogDebug $TmpScript
|
||||
|
||||
$Command | Out-File $TmpScript
|
||||
|
||||
$ArgumentList += @('-File', "$TmpScript")
|
||||
}
|
||||
else {
|
||||
$Bytes = [Text.Encoding]::Unicode.GetBytes($Command)
|
||||
$EncodedCommand = [Convert]::ToBase64String($Bytes)
|
||||
|
||||
Write-LogDebug $EncodedCommand
|
||||
|
||||
$ArgumentList += @('-EncodedCommand', $EncodedCommand)
|
||||
}
|
||||
|
||||
Write-LogDebug $ArgumentList
|
||||
|
||||
Write-Log "Starting external PowerShell process ..."
|
||||
|
||||
if ($Credential -eq $null) {
|
||||
$Process = Start-Process -FilePath 'powershell.exe' `
|
||||
-ArgumentList @($ArgumentList) `
|
||||
-RedirectStandardOutput $StdOut `
|
||||
-RedirectStandardError $StdErr `
|
||||
-NoNewWindow `
|
||||
-Wait `
|
||||
-PassThru
|
||||
}
|
||||
else {
|
||||
$Process = Start-Process -FilePath 'powershell.exe' `
|
||||
-ArgumentList @($ArgumentList) `
|
||||
-RedirectStandardOutput $StdOut `
|
||||
-RedirectStandardError $StdErr `
|
||||
-Credential $Credential `
|
||||
-NoNewWindow `
|
||||
-Wait `
|
||||
-PassThru
|
||||
}
|
||||
|
||||
Write-Log "External PowerShell process exited with exit code '$($Process.ExitCode)'."
|
||||
|
||||
#if ($ArgumentList -contains '-File') {
|
||||
# Remove-Item -Path $TmpScript -Force
|
||||
#}
|
||||
|
||||
$ErrorActionPreferenceSaved = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
|
||||
Write-LogDebug "StdOut file is '$StdOut'"
|
||||
Write-LogDebug "StdErr file is '$StdErr'"
|
||||
|
||||
if ((Get-Item $StdOut).Length -gt 0) {
|
||||
try {
|
||||
Write-LogDebug "Loading StdOut from '$StdOut'"
|
||||
$TmpFile = Select-CliXmlBlock $StdOut
|
||||
$StdOutObject = Import-Clixml $TmpFile
|
||||
Write-LogDebug "<StdOut>"
|
||||
Write-LogDebug ($StdOutObject)
|
||||
Write-LogDebug "</StdOut>"
|
||||
$StdOutObject
|
||||
#Remove-Item -Path $TmpFile -Force
|
||||
}
|
||||
catch {
|
||||
Write-LogDebug "An error occured while loading StdOut from '$TmpFile'"
|
||||
}
|
||||
}
|
||||
|
||||
if ((Get-Item $StdErr).Length -gt 0) {
|
||||
try {
|
||||
Write-LogDebug "Loading StdErr ..."
|
||||
$TmpFile = Select-CliXmlBlock $StdErr
|
||||
$StdErrObject = Import-Clixml $TmpFile
|
||||
Write-LogDebug "<StdErr>"
|
||||
Write-LogDebug ($StdErrObject)
|
||||
Write-LogDebug "</StdErr>"
|
||||
if (-not $IgnoreStdErr) {
|
||||
$StdErrObject
|
||||
}
|
||||
#Remove-Item -Path $TmpFile -Force
|
||||
}
|
||||
catch {
|
||||
Write-LogDebug "An error occured while loading StdErr from '$TmpFile'"
|
||||
}
|
||||
}
|
||||
|
||||
$ErrorActionPreference = $ErrorActionPreferenceSaved
|
||||
|
||||
if ($Process.ExitCode -ne 0) {
|
||||
throw("External PowerShell process exited with code '$($Process.ExitCode)'")
|
||||
}
|
||||
|
||||
#Remove-Item $StdOut -Force
|
||||
#Remove-Item $StdErr -Force
|
||||
}
|
||||
}
|
60
data/templates/agent/scripts/Update-ServiceConfig.ps1
Normal file
60
data/templates/agent/scripts/Update-ServiceConfig.ps1
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Update-ServiceConfig {
|
||||
param (
|
||||
[String] $Name,
|
||||
[String] $RunAsUser = '',
|
||||
[String] $DomainName = '.',
|
||||
[String] $Password = '',
|
||||
[Switch] $RunAsLocalService
|
||||
)
|
||||
begin {
|
||||
Show-InvocationInfo $MyInvocation
|
||||
}
|
||||
end {
|
||||
Show-InvocationInfo $MyInvocation -End
|
||||
}
|
||||
process {
|
||||
trap {
|
||||
&$TrapHandler
|
||||
}
|
||||
|
||||
$ArgumentList = @('config', "`"$Name`"")
|
||||
|
||||
if ($RunAsLocalService) {
|
||||
$ArgumentList += @("obj=", "`"NT AUTHORITY\LocalService`"")
|
||||
}
|
||||
elseif ($RunAsUser -ne '') {
|
||||
$ArgumentList += @("obj=", "`"$DomainName\$RunAsUser`"", "password=", "`"$Password`"")
|
||||
}
|
||||
|
||||
$Process = Exec 'sc.exe' $ArgumentList -PassThru -RedirectStreams
|
||||
|
||||
if ($Process.ExitCode -ne 0) {
|
||||
throw "Command 'sc.exe' returned exit code '$($Process.ExitCode)'"
|
||||
}
|
||||
|
||||
$NtRights = "C:\Murano\Tools\ntrights.exe"
|
||||
|
||||
if (-not ([IO.File]::Exists($NtRights))) {
|
||||
throw "File '$NtRights' not found."
|
||||
}
|
||||
|
||||
$Process = Exec $NtRights @('-u', "$DomainName\$RunAsUser", '+r', 'SeServiceLogonRight') -RedirectStreams -PassThru
|
||||
|
||||
if ($Process.ExitCode -ne 0) {
|
||||
throw "Command '$NtRights' returned exit code '$($Process.ExitCode)'"
|
||||
}
|
||||
|
||||
$Process = Exec $NtRights @('-u', "$DomainName\$RunAsUser", '+r', 'SeBatchLogonRight') -RedirectStreams -PassThru
|
||||
|
||||
if ($Process.ExitCode -ne 0) {
|
||||
throw "Command '$NtRights' returned exit code '$($Process.ExitCode)'"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import json
|
||||
import uuid
|
||||
import os
|
||||
|
||||
from muranoconductor.openstack.common import log as logging
|
||||
from muranocommon.messaging import Message
|
||||
@ -20,11 +21,14 @@ class WindowsAgentExecutor(CommandBase):
|
||||
|
||||
def execute(self, template, mappings, unit, service, callback,
|
||||
timeout=None):
|
||||
with open('data/templates/agent/%s.template' % template) as t_file:
|
||||
template_path = 'data/templates/agent/%s.template' % template
|
||||
with open(template_path) as t_file:
|
||||
template_data = t_file.read()
|
||||
|
||||
json_template = json.loads(template_data)
|
||||
json_template = self.encode_scripts(json_template, template_path)
|
||||
template_data = muranoconductor.helpers.transform_json(
|
||||
json.loads(template_data), mappings)
|
||||
json_template, mappings)
|
||||
|
||||
msg_id = str(uuid.uuid4()).lower()
|
||||
queue = ('%s-%s-%s' % (self._stack, service, unit)).lower()
|
||||
@ -42,6 +46,19 @@ class WindowsAgentExecutor(CommandBase):
|
||||
log.info('Sending RMQ message {0} to {1} with id {2}'.format(
|
||||
template_data, queue, msg_id))
|
||||
|
||||
def encode_scripts(self, json_data, template_path):
|
||||
scripts_folder = ''.join([os.path.dirname(template_path), "/scripts/"])
|
||||
script_files = json_data.get("Scripts", [])
|
||||
scripts = []
|
||||
for script in script_files:
|
||||
script_path = os.path.join(scripts_folder, script)
|
||||
log.debug('Loading script "{0}"'.format(script_path))
|
||||
with open(script_path) as script_file:
|
||||
script_data = script_file.read()
|
||||
scripts.append(script_data.encode('base64'))
|
||||
json_data["Scripts"] = scripts
|
||||
return json_data
|
||||
|
||||
def has_pending_commands(self):
|
||||
return len(self._pending_list) > 0
|
||||
|
||||
|
63
tests/conductor/test_windows_agent.py
Normal file
63
tests/conductor/test_windows_agent.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright (c) 2013 Mirantis Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import unittest
|
||||
import mock
|
||||
import mockfs
|
||||
import json
|
||||
|
||||
from muranoconductor.commands.windows_agent import WindowsAgentExecutor
|
||||
|
||||
|
||||
class TestWindowsAgent(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.mfs = mockfs.replace_builtins()
|
||||
self.template = {
|
||||
"Scripts": [
|
||||
"Get-DnsListeningIpAddress.ps1",
|
||||
"Join-Domain.ps1"
|
||||
],
|
||||
"Commands": [
|
||||
{
|
||||
"Name": "Get-DnsListeningIpAddress",
|
||||
"Arguments": {}
|
||||
}],
|
||||
"RebootOnCompletion": 0
|
||||
}
|
||||
|
||||
self.mfs.add_entries({
|
||||
'./data/templates/agent/test.template':
|
||||
json.dumps(self.template),
|
||||
'./data/templates/agent/scripts/Get-DnsListeningIpAddress.ps1':
|
||||
'function GetDNSip(){\ntest\n}\n',
|
||||
'./data/templates/agent/scripts/Join-Domain.ps1':
|
||||
'function JoinDomain(){\ntest\n}\n',
|
||||
})
|
||||
self.template_path = './data/templates/agent/test.template'
|
||||
|
||||
def test_script_encode(self):
|
||||
stack = mock.MagicMock()
|
||||
rmqclient = mock.MagicMock()
|
||||
reporter = mock.MagicMock()
|
||||
rmqclient.declare = mock.Mock()
|
||||
|
||||
executor = WindowsAgentExecutor(stack, rmqclient, reporter)
|
||||
result = executor.encode_scripts(self.template, self.template_path)
|
||||
encoded = [
|
||||
'ZnVuY3Rpb24gR2V0RE5TaXAoKXsKdGVzdAp9Cg==\n',
|
||||
'ZnVuY3Rpb24gSm9pbkRvbWFpbigpewp0ZXN0Cn0K\n'
|
||||
]
|
||||
self.assertEqual(result['Scripts'], encoded,
|
||||
'Encoded script is incorrect')
|
Loading…
x
Reference in New Issue
Block a user