├── .gitignore ├── LICENSE.txt ├── README.md ├── Vagrantfile ├── example.palette ├── images └── icon.png ├── nuget-packages └── choco │ ├── picassio.nuspec │ └── tools │ ├── chocolateyInstall.ps1 │ └── chocolateyUninstall.ps1 ├── src ├── Modules │ ├── bower.psm1 │ ├── cake.psm1 │ ├── certificate.psm1 │ ├── chef-client.psm1 │ ├── chocolatey.psm1 │ ├── command.psm1 │ ├── copy.psm1 │ ├── directory.psm1 │ ├── echo.psm1 │ ├── edison.psm1 │ ├── email.psm1 │ ├── git.psm1 │ ├── hosts.psm1 │ ├── iis-app-pool.psm1 │ ├── iis-binding.psm1 │ ├── iis-website.psm1 │ ├── iis.psm1 │ ├── ms-sql.psm1 │ ├── msbuild.psm1 │ ├── net-share.psm1 │ ├── nlb-node.psm1 │ ├── nodejs.psm1 │ ├── npm.psm1 │ ├── nuget.psm1 │ ├── nunit.psm1 │ ├── picassio.psm1 │ ├── ps-sql.psm1 │ ├── service.psm1 │ ├── ssdt.psm1 │ ├── svn.psm1 │ ├── vagrant.psm1 │ ├── variables.psm1 │ ├── windows-feature.psm1 │ └── windows-optional-feature.psm1 ├── Picassio.ps1 ├── Picassio.pssproj ├── Picassio.sln └── Tools │ └── PicassioTools.psm1 └── tests ├── bower.palette ├── chef-client.palette ├── chef-test-recipe.rb ├── chocolatey.palette ├── command.palette ├── directory.palette ├── eraseBeforePaint.palette ├── git.palette ├── ms-sql.palette ├── npm.palette ├── picassio.palette ├── rollbackOnFail.palette ├── variables.palette └── windows-feature.palette /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | build/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | 22 | # Vagrant 23 | .vagrant/ 24 | *.box 25 | 26 | # Chef 27 | tests/nodes 28 | 29 | # Roslyn cache directories 30 | *.ide/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | #NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding addin-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Others 158 | sql/ 159 | *.Cache 160 | ClientBin/ 161 | [Ss]tyle[Cc]op.* 162 | ~$* 163 | *~ 164 | *.dbmdl 165 | *.dbproj.schemaview 166 | *.pfx 167 | *.publishsettings 168 | node_modules/ 169 | 170 | # RIA/Silverlight projects 171 | Generated_Code/ 172 | 173 | # Backup & report files from converting an old project file 174 | # to a newer Visual Studio version. Backup files are not needed, 175 | # because we have git ;-) 176 | _UpgradeReport_Files/ 177 | Backup*/ 178 | UpgradeLog*.XML 179 | UpgradeLog*.htm 180 | 181 | # SQL Server files 182 | *.mdf 183 | *.ldf 184 | 185 | # Business Intelligence projects 186 | *.rdl.data 187 | *.bim.layout 188 | *.bim_*.settings 189 | 190 | # Microsoft Fakes 191 | FakesAssemblies/ 192 | 193 | # ========================= 194 | # Operating System Files 195 | # ========================= 196 | 197 | # OSX 198 | # ========================= 199 | 200 | .DS_Store 201 | .AppleDouble 202 | .LSOverride 203 | 204 | # Thumbnails 205 | ._* 206 | 207 | # Files that might appear on external disk 208 | .Spotlight-V100 209 | .Trashes 210 | 211 | # Directories potentially created on remote AFP share 212 | .AppleDB 213 | .AppleDesktop 214 | Network Trash Folder 215 | Temporary Items 216 | .apdisk 217 | 218 | # Windows 219 | # ========================= 220 | 221 | # Windows image file caches 222 | Thumbs.db 223 | ehthumbs.db 224 | 225 | # Folder config file 226 | Desktop.ini 227 | 228 | # Recycle Bin used on file shares 229 | $RECYCLE.BIN/ 230 | 231 | # Windows Installer files 232 | *.cab 233 | *.msi 234 | *.msm 235 | *.msp 236 | 237 | # Windows shortcuts 238 | *.lnk 239 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Matthew Kelly 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Picassio 2 | 3 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/Badgerati/Picassio/master/LICENSE.txt) 4 | 5 | > NOTE: I have started work a [`Picassio2`](https://github.com/Badgerati/Picassio2) which is code-over-config - making Picassio more flexible 6 | 7 | Picassio is a PowerShell v3.0+ automated provisioning/deployment tool, which uses a single JSON file to determine what commands to execute. 8 | 9 | Picassio is named so, as you take a just built empty server/computer and 'paint' it like a canvas using Picassio. The JSON file you pass in is called a 'palette' and this contains a 'paint' (with optional 'erase') object which is an array of 'colours'. 10 | 11 | All of Picassio's features (colours) are modularised, allowing for people to have the ability to create extension modules - explained at the end of this document. 12 | 13 | # Installing 14 | [Picassio](https://chocolatey.org/packages/picassio) can be installed via Chocolatey: 15 | 16 | ```bash 17 | choco install picassio 18 | ``` 19 | 20 | # Features 21 | The following are all supported by Picassio: 22 | 23 | * Install/upgrade/uninstall software/packages via Chocolatey, NPM, Bower or NuGet 24 | * Clone/checkout repositories from Git/SVN 25 | * Build projects/solutions using MSBuild 26 | * Run specified commands using either Command Prompt or PowerShell 27 | * Install/uninstall and stop/start Windows services 28 | * Copy files/folders with inclusions/exclusions 29 | * Create VMs using Vagrant 30 | * Add/remove entries from the hosts file 31 | * Add/remove website on IIS 32 | * Run node.js applications 33 | * Run tests via NUnit 34 | * Install/uninstall Windows (optional) features such as Web-Server for IIS 35 | * Ability to setup certificates in MMC 36 | * Run cake build scripts 37 | * Run SQL Server scripts or create/restore backups using PS-SQL 38 | * Run SQL scripts directly on a SQL Server 39 | * Can send emails 40 | * Ability to publish/generate scripts for SSDT 41 | * Support for Network Load Balancer 42 | * Extension modules can be written for third-parties 43 | * Run cookbooks and recipes using Chef 44 | 45 | # Dependencies 46 | Picassio doesn't depend on any external software to run however, when required it will automatically install the following for you: 47 | 48 | * Chocolatey 49 | * git 50 | * svn 51 | * Vagrant 52 | * node.js / npm 53 | * cake 54 | * NuGet 55 | * bower 56 | * Chef 57 | 58 | The above will only be installed when Picassio needs to use them. For example, using a Chocolatey type colour to install node.js will automatically install Chocolatey as well, or cloning a Git branch will auto-install Git if needed. 59 | 60 | To view the source code, you can see the ps1 scripts via: 61 | 62 | * PowerShell ISE 63 | * Your favourite text editor 64 | * Visual Studio (you will need the "PowerShell Tools" Visual Studio extension) 65 | 66 | # Examples 67 | To chain them together, just append more colour objects within the paint array. This way you can clone a branch from Git which is a simple WCF Service, build it and then install the service and start it. 68 | 69 | As a side note, each colour can have an optional "description" key-value. This value will get written to the console for informational purposes only, and to help you find specific sections in the log outputted. 70 | 71 | Note: You can see more examples in the `examples.palette` file bundled with the source code, or view each psm1 module for an example of how to use each one in the header. 72 | 73 | ## Running Picassio 74 | ```bash 75 | picassio -palette example.palette -validate 76 | picassio -palette example.palette -paint 77 | picassio -version 78 | picassio -help 79 | ``` 80 | 81 | Calling just `picassio -paint` in a directory will look for a default 'picassio.palette' file. 82 | 83 | ## Passing Credentials 84 | Picassio does have support for username/password credentials should you require them. There are two ways to set these credentials: 85 | 86 | * Specify the `-username` and `-password` arguments from the CLI 87 | * Add a colour of singular `{ "type": "credentials" }` somewhere in your paint/erase palette sections. When Picassio gets to this type, assuming you haven't already set the credentials, then the user is prompted to enter them 88 | 89 | The credentials are passed to every `Start-Module`, `Test-Module`, `Start-Extension` and `Test-Extension` call, and does have the possibility of being null. 90 | 91 | ## Palette Settings 92 | A palette has two sections: a paint and an erase section. The paint section is designed to deploy and provision the machine, where as the erase section should roll the machine back to a state before it was painted. 93 | 94 | When painting the machine it is possible for it to fail, and then you're left in a malformed state. Or what if you wish to roll everything back, and then paint the machine. 95 | 96 | Well, Picassio has the following three settings that can be supplied at the top of a palette: 97 | 98 | * rollbackOnFail: Runs the opposite section to the one that was just attempted, if the current one fails to run successfully. 99 | * eraseBeforePaint: Runs the erase section first, before painting the current machine. 100 | * eraseAfterPaint: Mostly for testing purposes, but will erase the machine after it has been painted. 101 | 102 | ```json 103 | { 104 | "rollbackOnFail": true, 105 | "eraseBeforePaint": false, 106 | "eraseAfterPaint": false, 107 | "paint": [ 108 | "..." 109 | ], 110 | "erase": [ 111 | "..." 112 | ] 113 | } 114 | ``` 115 | 116 | If none of the settings are supplied, then they are all defaulted to false. 117 | 118 | ## Installing a Service 119 | Something else Picassio can do it install/uninstall and stop/start Windows services. If you are installing a service then the absolute path to the installer in required however, if you are just uninstalling one then the path can be omitted. 120 | 121 | The following palette will install and start a service. 122 | ```json 123 | { 124 | "paint": [ 125 | { 126 | "type": "service", 127 | "name": "Test Service", 128 | "path": "C:\\absolute\\path\\to\\your\\service.exe", 129 | "ensure": "installed", 130 | "state": "started" 131 | } 132 | ] 133 | } 134 | ``` 135 | 136 | The following palette will uninstall a service. 137 | ```json 138 | { 139 | "paint": [ 140 | { 141 | "type": "service", 142 | "name": "Test Service", 143 | "ensure": "uninstalled" 144 | } 145 | ] 146 | } 147 | ``` 148 | 149 | If you are ensuring a service is installed and started, and it already is then the service will be restarted. 150 | 151 | ## Add/remove a website in IIS 152 | Picassio can add/remove, start/stop websites and application pools in IIS. You can also set-up binding for http/https IP/port setting. 153 | 154 | When a website is created, the default "*:80:" endpoint is removed, if this is required just specify the binding within the bindings array. 155 | 156 | Picassio will also add IIS and application pool users to the website path in IIS. This is so the ApplicationPoolIdentity and IIS users have permissions to see the website directory. 157 | 158 | The following palette will setup an entry into the hosts file, and also create a website/app pool in IIS. The website will be accessible from 127.0.0.2 or test.site.com. 159 | ```json 160 | { 161 | "paint": [ 162 | { 163 | "type": "hosts", 164 | "ensure": "added", 165 | "ip": "127.0.0.2", 166 | "hostname": "test.site.com" 167 | }, 168 | { 169 | "type": "iis", 170 | "ensure": "added", 171 | "state": "started", 172 | "siteName": "Test Website", 173 | "appPoolName": "Test Website", 174 | "path": "C:\\Website\\TestWebsite", 175 | "bindings": [ 176 | { 177 | "ip": "127.0.0.2", 178 | "port": "80", 179 | "protocol": "http" 180 | } 181 | ] 182 | } 183 | ] 184 | } 185 | ``` 186 | 187 | If you use a binding of 'https' you'll also need to pass a "certificate" key-value in the bindings. So if above we used https, a possible certificate could be "*.site.com". 188 | 189 | ## Add/remove website bindings in IIS 190 | If you already have a website setup in IIS, then Picassio can add/remove binding to an already existing website. 191 | 192 | The following palette will add an http binding to a website. This is rather similar to the binding array for a website: 193 | ```json 194 | { 195 | "paint": [ 196 | { 197 | "type": "iis-binding", 198 | "ensure": "added", 199 | "siteName": "Test Website", 200 | "ip": "127.0.0.2", 201 | "port": "80", 202 | "protocol": "http" 203 | } 204 | ] 205 | } 206 | ``` 207 | 208 | Again like above, if you use a binding of 'https' you'll also need to pass a "certificate" key-value in the bindings. So if above we used https, a possible certificate could be "*.site.com". 209 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.box = "opentable/win-2012r2-standard-amd64-nocm" 3 | config.vm.guest = :windows 4 | config.vm.communicator = "winrm" 5 | 6 | config.vm.provider "virtualbox" do |vb| 7 | vb.gui = true 8 | vb.customize ["modifyvm", :id, "--clipboard", "bidirectional"] 9 | end 10 | 11 | config.vm.provision "shell" do |s| 12 | s.inline = "winrm quickconfig" 13 | end 14 | 15 | config.vm.provision "shell", run: "always" do |s| 16 | s.inline = "cd c:/vagrant/src; ./picassio.ps1 -install; refreshenv" 17 | s.keep_color = true 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /example.palette: -------------------------------------------------------------------------------- 1 | { 2 | "rollbackOnFail": false, 3 | "eraseBeforePaint": false, 4 | "eraseAfterPaint": false, 5 | "paint": [ 6 | { 7 | "type": "variables", 8 | "variables": { 9 | "value1": true, 10 | "value2": "added" 11 | } 12 | }, 13 | { 14 | "type": "credentials", 15 | "message": "Picassio requires your credentials" 16 | }, 17 | { 18 | "type": "chocolatey", 19 | "ensure": "install", 20 | "software": { 21 | "git": "latest", 22 | "curl": "latest" 23 | } 24 | }, 25 | { 26 | "type": "git", 27 | "remote": "https://github.com/Badgerati/Table.git", 28 | "path": "C:\\Projects\\PicassoTest", 29 | "name": "RenamedFolder", 30 | "branch": "master" 31 | }, 32 | { 33 | "type": "msbuild", 34 | "toolpath": "C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\MSBuild.exe", 35 | "configuration": "Debug", 36 | "projects": [ "C:\\Projects\\PicassoTest\\RenamedFolder\\C#\\Table.sln" ], 37 | "arguments": "/t:Rebuild", 38 | "clean": false 39 | }, 40 | { 41 | "type": "command", 42 | "prompt": "powershell", 43 | "command": "echo Hello, world!" 44 | }, 45 | { 46 | "type": "service", 47 | "name": "Test Service", 48 | "ensure": "exists", 49 | "state": "stopped" 50 | }, 51 | { 52 | "type": "service", 53 | "name": "Test Service", 54 | "path": "C:\\full\\absoulte\\path\\to\\service.exe", 55 | "ensure": "installed", 56 | "state": "started" 57 | }, 58 | { 59 | "type": "copy", 60 | "from": "C:\\path\\to\\some\\folder", 61 | "to": "C:\\path\\to\\some\\other\\folder", 62 | "excludeFiles": [ "*.html", "*.js" ], 63 | "includeFolders": [ "src" ] 64 | }, 65 | { 66 | "type": "vagrant", 67 | "path": "C:\\path\\to\\project", 68 | "command": "up" 69 | }, 70 | { 71 | "type": "hosts", 72 | "ensure": "add", 73 | "ip": "127.0.0.3", 74 | "hostname": "test.local.com" 75 | }, 76 | { 77 | "type": "hosts", 78 | "ensure": "add", 79 | "ip": "127.0.0.2", 80 | "hostname": "test.site.com" 81 | }, 82 | { 83 | "type": "iis", 84 | "ensure": "added", 85 | "state": "started", 86 | "siteName": "Test Website", 87 | "appPoolName": "Test Website", 88 | "path": "C:\\Website\\TestWebsite", 89 | "bindings": [ 90 | { 91 | "ip": "127.0.0.2", 92 | "port": "80", 93 | "protocol": "http" 94 | } 95 | ] 96 | }, 97 | { 98 | "type": "nunit", 99 | "toolpath": "C:\\Program Files\\NUnit\\bin\\nunit-2.0\\nunit-console.exe", 100 | "arguments": "/include:UnitTest,PeformanceTest /nologo", 101 | "tests": [ 102 | "Example\\Test1.dll", 103 | "Example\\Test2.dll" 104 | ] 105 | }, 106 | { 107 | "type": "ps-sql", 108 | "timeout": 60, 109 | "server": "SOME_SERVER_1", 110 | "snapshot": { 111 | "type": "create", 112 | "name": "Example_Snapshot", 113 | "location": "C:\\example\\path", 114 | "database": "[Example]" 115 | } 116 | }, 117 | { 118 | "type": "ps-sql", 119 | "timeout": 60, 120 | "server": "SOME_SERVER_1", 121 | "backup": { 122 | "type": "restore", 123 | "location": "C:\\example\\path", 124 | "database": "[Example]", 125 | "sqlpath": "C:\\" 126 | } 127 | }, 128 | { 129 | "type": "ps-sql", 130 | "timeout": 60, 131 | "server": "SOME_SERVER_1", 132 | "sqlpsPath": "C:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\", 133 | "sql": { 134 | "query": "SELECT COUNT(1) FROM [Example]" 135 | } 136 | }, 137 | { 138 | "type": "certificate", 139 | "ensure": "exported", 140 | "certStoreType": "LocalMachine", 141 | "certStoreName": "My", 142 | "certificate": "SomeCert", 143 | "certType": "CERT", 144 | "certPath": "C:\\some-cert.cer" 145 | }, 146 | { 147 | "type": "certificate", 148 | "ensure": "imported", 149 | "certStoreType": "LocalMachine", 150 | "certStoreName": "Root", 151 | "certPath": "C:\\some-cert.cer" 152 | } 153 | ], 154 | "erase": [ 155 | { 156 | "type": "chocolatey", 157 | "ensure": "uninstall", 158 | "software": { 159 | "git": "", 160 | "curl": "" 161 | } 162 | }, 163 | { 164 | "type": "hosts", 165 | "ensure": "remove", 166 | "ip": "127.0.0.3", 167 | "hostname": "test.local.com" 168 | } 169 | ] 170 | } 171 | -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Badgerati/Picassio/b19732eba9edd998ddf351663e5f31c5b0ca4920/images/icon.png -------------------------------------------------------------------------------- /nuget-packages/choco/picassio.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | picassio 6 | Picassio 7 | $version$ 8 | Badgerati 9 | Badgerati 10 | Picassio is a PowerShell automated provisioning/deployment tool for Windows 11 | 12 | Picassio is a PowerShell v3.0+ automated provisioning/deployment tool, which uses a single JSON file to determine what commands to execute. 13 | 14 | ### Features 15 | The following are all supported by Picassio: 16 | 17 | * Install/upgrade/uninstall software/packages via Chocolatey, NPM, Bower or NuGet 18 | * Clone/checkout repositories from Git/SVN 19 | * Build projects/solutions using MSBuild 20 | * Run specified commands using either Command Prompt or PowerShell 21 | * Install/uninstall and stop/start Windows services 22 | * Copy files/folders with inclusions/exclusions 23 | * Create VMs using Vagrant 24 | * Add/remove entries from the hosts file 25 | * Add/remove website on IIS 26 | * Run node.js applications 27 | * Run tests via NUnit 28 | * Install/uninstall Windows (optional) features such as Web-Server for IIS 29 | * Ability to setup certificates in MMC 30 | * Run cake build scripts 31 | * Run SQL Server scripts or create/restore backups using PS-SQL 32 | * Run SQL scripts directly on a SQL Server 33 | * Can send emails 34 | * Ability to publish/generate scripts for SSDT 35 | * Support for Network Load Balancer 36 | * Extension modules can be written for third-parties 37 | * Run cookbooks and recipes using Chef 38 | 39 | https://github.com/Badgerati/Picassio 40 | https://github.com/Badgerati/Picassio/tree/master/nuget-packages 41 | https://github.com/Badgerati/Picassio/wiki 42 | https://github.com/Badgerati/Picassio/issues 43 | picassio powershell devops json deploy build provision automate test install software iis admin 44 | Copyright 2016 45 | https://github.com/Badgerati/Picassio/blob/master/LICENSE.txt 46 | false 47 | https://cdn.rawgit.com/Badgerati/Picassio/master/images/icon.png 48 | https://github.com/Badgerati/Picassio/releases 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /nuget-packages/choco/tools/chocolateyInstall.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = 'Stop'; 2 | 3 | $packageName= 'Picassio' 4 | $toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 5 | $url = 'https://github.com/Badgerati/Picassio/releases/download/v$version$/$version$-Binaries.zip' 6 | 7 | $packageArgs = @{ 8 | packageName = $packageName 9 | unzipLocation = $toolsDir 10 | url = $url 11 | } 12 | 13 | # Download Picassio 14 | Install-ChocolateyZipPackage @packageArgs 15 | 16 | # Install Picassio 17 | $picassioPath = Join-Path $env:chocolateyPackageFolder 'tools/src' 18 | Push-Location $picassioPath 19 | 20 | try 21 | { 22 | $main = $pwd 23 | $tools = "$main\Tools" 24 | $modules = "$main\Modules" 25 | $extensions = "$main\Extensions" 26 | 27 | if (!(Test-Path $extensions)) 28 | { 29 | Write-Host "Creating '$extensions' directory." 30 | New-Item -ItemType Directory -Force -Path $extensions | Out-Null 31 | } 32 | 33 | Write-Host 'Updating environment Path.' 34 | Install-ChocolateyPath -PathToInstall $main -PathType 'Machine' 35 | 36 | Write-Host 'Creating environment variables.' 37 | Install-ChocolateyEnvironmentVariable -VariableName 'PicassioModules' -VariableValue $modules -VariableType 'Machine' 38 | Install-ChocolateyEnvironmentVariable -VariableName 'PicassioExtensions' -VariableValue $extensions -VariableType 'Machine' 39 | 40 | $tools = "$tools\PicassioTools.psm1" 41 | Install-ChocolateyEnvironmentVariable -VariableName 'PicassioTools' -VariableValue $tools -VariableType 'Machine' 42 | 43 | refreshenv 44 | } 45 | finally 46 | { 47 | Pop-Location 48 | } 49 | -------------------------------------------------------------------------------- /nuget-packages/choco/tools/chocolateyUninstall.ps1: -------------------------------------------------------------------------------- 1 | # Uninstall Picassio 2 | $picassioPath = Join-Path $env:chocolateyPackageFolder 'tools/src' 3 | 4 | Write-Host 'Removing Picassio from environment Path.' 5 | if (($env:Path.Contains($picassioPath))) 6 | { 7 | $current = (Get-EnvironmentVariable -Name 'PATH' -Scope 'Machine') 8 | $current = $current.Replace($picassioPath, [string]::Empty) 9 | Set-EnvironmentVariable -Name 'PATH' -Value $current -Scope 'Machine' 10 | $env:PATH = $current 11 | } 12 | 13 | Write-Host 'Removing environment variables.' 14 | Uninstall-ChocolateyEnvironmentVariable -VariableName 'PicassioModules' -VariableType 'Machine' 15 | Uninstall-ChocolateyEnvironmentVariable -VariableName 'PicassioExtensions' -VariableType 'Machine' 16 | Uninstall-ChocolateyEnvironmentVariable -VariableName 'PicassioTools' -VariableType 'Machine' 17 | 18 | refreshenv 19 | -------------------------------------------------------------------------------- /src/Modules/bower.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "bower", 15 | # "ensure": "install", 16 | # "args": "--save", 17 | # "packages": { 18 | # "jquery": "1.8.2", 19 | # "normalize.css": "" 20 | # }, 21 | # "path": "C:\\to\\install\\packages" 22 | # } 23 | # ] 24 | # } 25 | ######################################################################### 26 | 27 | # Uses bower to install, upgrade or uninstall the speicified packages 28 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 29 | 30 | function Start-Module($colour, $variables, $credentials) 31 | { 32 | Test-Module $colour $variables $credentials 33 | 34 | # Check to see if git is installed, if not then install it 35 | if (!(Test-Software 'git --version' 'git')) 36 | { 37 | Write-Warnings 'Git is not installed' 38 | Install-AdhocSoftware 'git.install' 'Git' 39 | } 40 | 41 | # Check to see if bower is installed, if not then install it 42 | if (!(Test-Software 'bower help')) 43 | { 44 | Write-Warnings 'bower is not installed' 45 | Install-AdhocSoftware 'bower' 'bower' 'npm' 46 | } 47 | 48 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 49 | $packages = $colour.packages 50 | 51 | $path = Replace-Variables $colour.path $variables 52 | if ([string]::IsNullOrWhiteSpace($path)) 53 | { 54 | $path = [string]::Empty 55 | } 56 | elseif (!(Test-Path $path)) 57 | { 58 | throw "Path does not exist: '$path'." 59 | } 60 | 61 | $_args = Replace-Variables $colour.args $variables 62 | if ($_args -eq $null) 63 | { 64 | $_args = [string]::Empty 65 | } 66 | 67 | # Set location as path pass to bower installed 68 | if (![string]::IsNullOrWhiteSpace($path)) 69 | { 70 | Push-Location $path 71 | } 72 | 73 | try 74 | { 75 | if ($packages -eq $null) 76 | { 77 | Write-Message "Starting bower $ensure." 78 | 79 | Run-Command 'bower' "$ensure $_args" 80 | 81 | Write-Message "bower $ensure successful." 82 | Reset-Path $false 83 | } 84 | else 85 | { 86 | $keys = $packages.psobject.properties.name 87 | ForEach ($key in $keys) 88 | { 89 | # Grab the package we're dealing with currently 90 | $key = (Replace-Variables $key $variables).Trim() 91 | $normKey = $key -replace '\.', '-' 92 | 93 | # What version of that package do we need? 94 | $version = Replace-Variables $packages.$key $variables 95 | if ([string]::IsNullOrWhiteSpace($version)) 96 | { 97 | $version = [string]::Empty 98 | } 99 | 100 | $version = $version.Trim() 101 | 102 | # Store the current ensure value, as this might be changed to update later 103 | $current_ensure = $ensure 104 | 105 | # Deal with the ensure value 106 | $nothing_to_do = $false 107 | switch ($current_ensure) 108 | { 109 | 'install' 110 | { 111 | if (![string]::IsNullOrWhiteSpace($version)) 112 | { 113 | $result = (bower list | Where-Object { $_ -ilike "*$normKey#$version*" } | Measure-Object).Count 114 | if ($result -ne 0) 115 | { 116 | Write-Information "$key#$version is already installed." 117 | $nothing_to_do = $true 118 | } 119 | } 120 | 121 | if (!$nothing_to_do) 122 | { 123 | $result = (bower list | Where-Object { $_ -ilike "*$normKey*" } | Measure-Object).Count 124 | if ($result -ne 0) 125 | { 126 | $current_ensure = 'update' 127 | } 128 | } 129 | } 130 | 131 | 'uninstall' 132 | { 133 | $result = (bower list | Where-Object { $_ -ilike "*$normKey*" } | Measure-Object).Count 134 | if ($result -eq 0) 135 | { 136 | Write-Information "$key is already uninstalled." 137 | $nothing_to_do = $true 138 | } 139 | } 140 | } 141 | 142 | # If package already (un)installed, skip to next 143 | if ($nothing_to_do) 144 | { 145 | continue 146 | } 147 | 148 | if ([string]::IsNullOrWhiteSpace($version) -or $current_ensure -ieq 'uninstall') 149 | { 150 | $version = [string]::Empty 151 | $versionStr = 'latest' 152 | } 153 | else 154 | { 155 | $versionStr = $version 156 | $version = "#$version" 157 | } 158 | 159 | Write-Message "Starting $current_ensure of $key, version: $versionStr" 160 | 161 | Run-Command 'bower' "$current_ensure $key$version $_args" $false $true 162 | 163 | Write-Message "$current_ensure of $key ($versionStr) successful." 164 | Reset-Path $false 165 | Write-NewLine 166 | } 167 | } 168 | } 169 | finally 170 | { 171 | if (![string]::IsNullOrWhiteSpace($path)) 172 | { 173 | Pop-Location 174 | } 175 | } 176 | } 177 | 178 | function Test-Module($colour, $variables, $credentials) 179 | { 180 | # Get ensured operation for installing/uninstalling 181 | $ensure = Replace-Variables $colour.ensure $variables 182 | $ensures = @('install', 'uninstall') 183 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 184 | { 185 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 186 | } 187 | 188 | # Check to see if any packages were supplied 189 | $packages = $colour.packages 190 | if ($packages -ne $null) 191 | { 192 | # Grab the names of the packages, ensure we have valid values 193 | $keys = $packages.psobject.properties.name 194 | if ($keys -eq $null -or $keys.Length -eq 0) 195 | { 196 | throw 'No package names have been supplied.' 197 | } 198 | 199 | if (($keys | Where-Object { [string]::IsNullOrWhiteSpace((Replace-Variables $_ $variables)) } | Measure-Object).Count -gt 0) 200 | { 201 | throw 'Invalid or empty package names found.' 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/Modules/cake.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "cake", 15 | # "path": "C:\\path\\to\\project", 16 | # "name": "build.cake" 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Calls cake build from the specified path where a cake script can be found 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | if (!(Test-Software 'cake.exe -version' 'cake')) 30 | { 31 | Write-Warnings 'Cake is not installed' 32 | Install-AdhocSoftware 'cake.portable' 'Cake' 33 | } 34 | 35 | $path = (Replace-Variables $colour.path $variables).Trim() 36 | if (!(Test-Path $path)) 37 | { 38 | throw "Path specified to project doesn't exist: '$path'." 39 | } 40 | 41 | $name = (Replace-Variables $colour.name $variables).Trim() 42 | if ([string]::IsNullOrWhiteSpace($name)) 43 | { 44 | $name = 'build.cake' 45 | } 46 | 47 | if (!(Test-Path (Join-Path $path $name))) 48 | { 49 | throw "Cake build script does not exist at path: '$name'." 50 | } 51 | 52 | Write-Message 'Running Cake.' 53 | Push-Location $path 54 | 55 | try 56 | { 57 | Run-Command 'cake.exe' $name 58 | } 59 | finally 60 | { 61 | Pop-Location 62 | } 63 | 64 | Write-Message 'Cake build was successful.' 65 | } 66 | 67 | function Test-Module($colour, $variables, $credentials) 68 | { 69 | $path = Replace-Variables $colour.path $variables 70 | if ([string]::IsNullOrWhiteSpace($path)) 71 | { 72 | throw 'No path specified to a directory where the project is located.' 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Modules/certificate.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # { 10 | # "paint": [ 11 | # { 12 | # "type": "certificate", 13 | # "esnure": "imported", 14 | # "certStoreType: "LocalMachine", 15 | # "certStoreName": "Root", 16 | # "certPath": "C:\\path\\to\\cert.cer" 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Imports/Exports a certificate on MMC 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | $certStoreType = (Replace-Variables $colour.certStoreType $variables).Trim() 30 | $certStoreName = (Replace-Variables $colour.certStoreName $variables).Trim() 31 | if (!(Test-Path "Cert:\$certStoreType\$certStoreName")) 32 | { 33 | throw "Cetificate store does not exist 'Cert:\$certStoreType\$certStoreName'." 34 | } 35 | 36 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 37 | $certPass = (Replace-Variables $colour.certPass $variables) 38 | $certPath = (Replace-Variables $colour.certPath $variables).Trim() 39 | 40 | switch ($ensure) 41 | { 42 | 'exported' 43 | { 44 | $certificate = (Replace-Variables $colour.certificate $variables).Trim() 45 | Write-Message "Exporting certificate '$certificate' to '$certPath'." 46 | 47 | $certs = (Get-ChildItem "Cert:\$certStoreType\$certStoreName" | Where-Object { $_.Subject -match $certificate } | Select-Object -First 1) 48 | 49 | if ([string]::IsNullOrWhiteSpace($certs)) 50 | { 51 | throw "Certificate pattern passed cannot be found for exporting certificate: '$certificate'." 52 | } 53 | 54 | $certType = (Replace-Variables $colour.certType $variables).ToLower().Trim() 55 | 56 | ForEach ($cert in $certs) 57 | { 58 | switch ($certType) 59 | { 60 | 'cert' 61 | { 62 | $bytes = $cert.Export('CERT') 63 | } 64 | 65 | 'pfx' 66 | { 67 | $bytes = $cert.Export('PFX', $certPass) 68 | } 69 | } 70 | 71 | [System.IO.File]::WriteAllBytes($certPath, $bytes) 72 | } 73 | 74 | if (!$?) 75 | { 76 | throw 77 | } 78 | 79 | Write-Message 'Certificate exported successfully.' 80 | } 81 | 82 | 'imported' 83 | { 84 | Write-Message "Importing certificate '$certPath' to '$certStoreType\$certStoreName'." 85 | 86 | if (!(Test-Path $certPath)) 87 | { 88 | throw "Certificate path does not exist: '$certPath'." 89 | } 90 | 91 | $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 92 | 93 | if ([string]::IsNullOrEmpty($certPass)) 94 | { 95 | $cert.Import($certPath) 96 | } 97 | else 98 | { 99 | $cert.Import($certPath, $certPass, 'Exportable, PersistKeySet') 100 | } 101 | 102 | if (!$?) 103 | { 104 | throw 105 | } 106 | 107 | $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($certStoreName, $certStoreType) 108 | $store.Open("MaxAllowed") 109 | $store.Add($cert) 110 | if (!$?) 111 | { 112 | throw 113 | } 114 | 115 | $store.Close() 116 | if (!$?) 117 | { 118 | throw 119 | } 120 | 121 | Write-Message 'Certificate imported successfully.' 122 | } 123 | } 124 | } 125 | 126 | function Test-Module($colour, $variables, $credentials) 127 | { 128 | # check we have a valid cert store type 129 | $certStoreType = (Replace-Variables $colour.certStoreType $variables) 130 | if ([string]::IsNullOrEmpty($certStoreType)) 131 | { 132 | throw 'No certificate store type has been supplied.' 133 | } 134 | 135 | $certStoreType = $certStoreType.Trim() 136 | 137 | # check we have a valid cert store name 138 | $certStoreName = (Replace-Variables $colour.certStoreName $variables) 139 | if ([string]::IsNullOrEmpty($certStoreName)) 140 | { 141 | throw 'No certificate store name has been supplied.' 142 | } 143 | 144 | $certStoreName = $certStoreName.Trim() 145 | 146 | # check we have a valid cert path to export to 147 | $certPath = (Replace-Variables $colour.certPath $variables) 148 | if ([string]::IsNullOrEmpty($certPath)) 149 | { 150 | throw 'No certificate path has been supplied.' 151 | } 152 | 153 | # check we have a valid ensure property 154 | $ensure = (Replace-Variables $colour.ensure $variables) 155 | if ([string]::IsNullOrWhiteSpace($ensure)) 156 | { 157 | throw 'No ensure parameter supplied for certificate.' 158 | } 159 | 160 | $ensure = $ensure.ToLower().Trim() 161 | if ($ensure -ne 'imported' -and $ensure -ne 'exported') 162 | { 163 | throw "Invalid ensure supplied for certificate: '$ensure'." 164 | } 165 | 166 | if ($ensure -eq 'exported') 167 | { 168 | $certificate = (Replace-Variables $colour.certificate $variables) 169 | if ([string]::IsNullOrWhiteSpace($certificate)) 170 | { 171 | throw 'No certificate pattern passed for exporting certificate.' 172 | } 173 | 174 | $certType = (Replace-Variables $colour.certType $variables) 175 | if ([string]::IsNullOrWhiteSpace($certificate)) 176 | { 177 | throw 'No certificate type passed for exporting certificate.' 178 | } 179 | 180 | $certType = $certType.ToLower().Trim() 181 | if ($certType -ne 'cert' -and $certType -ne 'pfx') 182 | { 183 | throw "Invalid certificate type supplied for exporting: '$certType'." 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/Modules/chef-client.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "chef-client", 15 | # "path": "C:\\path\\to\\cookbook", 16 | # "cookbook": "dev", 17 | # "arguments": "--force-formatter", 18 | # "runList": [ 19 | # "web", 20 | # "sql" 21 | # ] 22 | # }, 23 | # { 24 | # "type": "chef-client", 25 | # "path": "C:\\path\\to\\recipes", 26 | # "arguments": "--force-formatter", 27 | # "recipes": [ 28 | # "web.rb", 29 | # "sql.rb" 30 | # ] 31 | # } 32 | # ] 33 | # } 34 | ######################################################################### 35 | 36 | # Run a cookbook using the chef-client tool 37 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 38 | 39 | function Start-Module($colour, $variables, $credentials) 40 | { 41 | Test-Module $colour $variables $credentials 42 | 43 | if (!(Test-Software 'chef-client --version' 'chef-client')) 44 | { 45 | Write-Warnings 'Chef is not installed' 46 | Install-AdhocSoftware 'chef-client' 'Chef' 47 | } 48 | 49 | $path = (Replace-Variables $colour.path $variables).Trim() 50 | $cookbook = Replace-Variables $colour.cookbook $variables 51 | $runList = $colour.runList 52 | $recipes = $colour.recipes 53 | 54 | $_args = Replace-Variables $colour.arguments $variables 55 | if ([string]::IsNullOrWhiteSpace($_args)) 56 | { 57 | $_args = [string]::Empty 58 | } 59 | 60 | Push-Location $path 61 | 62 | try 63 | { 64 | if ([string]::IsNullOrWhiteSpace($cookbook)) 65 | { 66 | ForEach ($recipe in $recipes) 67 | { 68 | Write-Message "Running Chef for recipe: $recipe" 69 | Run-Command 'chef-client' "--local-mode $recipe $_args" 70 | } 71 | } 72 | else 73 | { 74 | if ($runList -eq $null -or $runList.Length -eq 0) 75 | { 76 | Write-Message "Running Chef for $cookbook cookbook on default recipe." 77 | Run-Command 'chef-client' "--local-mode --runlist 'recipe[$cookbook]' $_args" 78 | } 79 | else 80 | { 81 | ForEach ($recipe in $runList) 82 | { 83 | Write-Message "Running Chef for $cookbook cookbook on $recipe recipe." 84 | Run-Command 'chef-client' ("--local-mode --runlist 'recipe[{0}::{1}]' {2}" -f $cookbook, $recipe, $_args) 85 | } 86 | } 87 | } 88 | } 89 | finally 90 | { 91 | Pop-Location 92 | } 93 | 94 | Write-Message 'Chef ran successfully.' 95 | } 96 | 97 | function Test-Module($colour, $variables, $credentials) 98 | { 99 | # Check the path 100 | $path = Replace-Variables $colour.path $variables 101 | if ([string]::IsNullOrWhiteSpace($path)) 102 | { 103 | throw 'No path passed to cookbooks/recipes.' 104 | } 105 | 106 | if (!(Test-Path $path) -and $variables['__initial_validation__'] -eq $false) 107 | { 108 | throw "Path to cookbooks/recipes does not exist: '$path'." 109 | } 110 | 111 | # Check the cookbook and runlist 112 | $cookbook = Replace-Variables $colour.cookbook $variables 113 | if (![string]::IsNullOrWhiteSpace($cookbook)) 114 | { 115 | $runList = $colour.runList 116 | if ($runList -ne $null -and $runList.Length -gt 0) 117 | { 118 | ForEach ($item in $runList) 119 | { 120 | $item = Replace-Variables $item $variables 121 | 122 | if ([string]::IsNullOrWhiteSpace($item)) 123 | { 124 | throw 'Cannot pass an empty recipe name for cookbook in runList.' 125 | } 126 | } 127 | } 128 | } 129 | 130 | # Check the recipes 131 | if ([string]::IsNullOrWhiteSpace($cookbook)) 132 | { 133 | $recipes = $colour.recipes 134 | if ($recipes -eq $null -or $recipes.Length -eq 0) 135 | { 136 | throw 'No recipes supplied to run.' 137 | } 138 | 139 | ForEach ($recipe in $recipes) 140 | { 141 | $recipe = Replace-Variables $recipe $variables 142 | 143 | if ([string]::IsNullOrWhiteSpace($recipe)) 144 | { 145 | throw 'Cannot pass an empty recipe name.' 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/Modules/chocolatey.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "chocolatey", 15 | # "ensure": "install", 16 | # "software": { 17 | # "git.install": "latest", 18 | # "atom": "1.0.7" 19 | # } 20 | # } 21 | # ] 22 | # } 23 | ######################################################################### 24 | 25 | # Uses Chocolatey to install, upgrade or uninstall the speicified softwares 26 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 27 | 28 | function Start-Module($colour, $variables, $credentials) 29 | { 30 | Test-Module $colour $variables $credentials 31 | 32 | # Grab the speech object 33 | $speech = $variables['__speech__'] 34 | 35 | # Check to see if Chocolatey is installed, if not then install it 36 | if (!(Test-Software 'choco -v')) 37 | { 38 | Install-Chocolatey 39 | } 40 | 41 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 42 | $ignoreFailure = [bool](Replace-Variables $colour.ignoreFailure $variables) 43 | $software = $colour.software 44 | $keys = $software.psobject.properties.name 45 | 46 | ForEach ($key in $keys) 47 | { 48 | # Grab the software we're dealing with currently 49 | $key = (Replace-Variables $key $variables).Trim() 50 | 51 | # What version of that software do we need? 52 | $version = Replace-Variables $software.$key $variables 53 | if ([string]::IsNullOrWhiteSpace($version)) 54 | { 55 | $version = 'latest' 56 | } 57 | 58 | $version = $version.Trim() 59 | 60 | # Store the current ensure value, as this might be changed to upgrade later 61 | $current_ensure = $ensure 62 | 63 | # Deal with the ensure value 64 | $nothing_to_do = $false 65 | switch ($current_ensure) 66 | { 67 | 'install' 68 | { 69 | if ($version -ine 'latest') 70 | { 71 | $result = (choco.exe list -lo | Where-Object { $_ -ilike "*$key*$version*" } | Measure-Object).Count 72 | if ($result -ne 0) 73 | { 74 | Write-Information "$key $version is already installed." $speech 75 | $nothing_to_do = $true 76 | } 77 | } 78 | 79 | if (!$nothing_to_do) 80 | { 81 | $result = (choco.exe list -lo | Where-Object { $_ -ilike "*$key*" } | Measure-Object).Count 82 | if ($result -ne 0) 83 | { 84 | $current_ensure = 'upgrade' 85 | } 86 | } 87 | } 88 | 89 | 'uninstall' 90 | { 91 | $result = (choco.exe list -lo | Where-Object { $_ -ilike "*$key*" } | Measure-Object).Count 92 | if ($result -eq 0) 93 | { 94 | Write-Information "$key is already uninstalled" $speech 95 | $nothing_to_do = $true 96 | } 97 | } 98 | } 99 | 100 | # If software already (un)installed, skip to next 101 | if ($nothing_to_do) 102 | { 103 | continue 104 | } 105 | 106 | if ($version -ieq 'latest' -or $current_ensure -ieq 'uninstall') 107 | { 108 | $versionTag = [string]::Empty 109 | $version = [string]::Empty 110 | $versionStr = 'latest' 111 | } 112 | else 113 | { 114 | $versionTag = '--version' 115 | $versionStr = $version 116 | } 117 | 118 | Write-Message "Starting $current_ensure of $key, version: $versionStr" $speech 119 | 120 | Run-Command 'choco.exe' "$current_ensure $key $versionTag $version -y" -ignoreFailure $ignoreFailure 121 | 122 | Write-Message "$current_ensure of $key ($versionStr) successful." $speech 123 | Reset-Path $false 124 | Write-NewLine 125 | } 126 | } 127 | 128 | function Test-Module($colour, $variables, $credentials) 129 | { 130 | # Get ensured operation for installing/uninstalling 131 | $ensure = Replace-Variables $colour.ensure $variables 132 | $ensures = @('install', 'uninstall') 133 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 134 | { 135 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 136 | } 137 | 138 | # Check to see if any software was supplied 139 | $software = $colour.software 140 | if ($software -eq $null) 141 | { 142 | throw 'No software has been supplied.' 143 | } 144 | 145 | # Grab the names of the software, ensure we have valid values 146 | $keys = $software.psobject.properties.name 147 | if ($keys -eq $null -or $keys.Length -eq 0) 148 | { 149 | throw 'No software names have been supplied.' 150 | } 151 | 152 | if (($keys | Where-Object { [string]::IsNullOrWhiteSpace((Replace-Variables $_ $variables)) } | Measure-Object).Count -gt 0) 153 | { 154 | throw 'Invalid or empty software names found.' 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Modules/command.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "command", 15 | # "prompt": "powershell", 16 | # "command": "Write-Host 'Hello, world!'" 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Run a passed command using Command Prompt/PowerShell 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | $command = (Replace-Variables $colour.command $variables).Trim() 30 | 31 | $prompt = Replace-Variables $colour.prompt $variables 32 | if ([string]::IsNullOrWhiteSpace($prompt)) 33 | { 34 | Write-Message 'No prompt type passed, defaulting to Command Prompt.' 35 | $prompt = 'cmd' 36 | } 37 | 38 | $path = Replace-Variables $colour.path $variables 39 | if (![string]::IsNullOrWhiteSpace($path)) 40 | { 41 | if (!(Test-Path $path)) 42 | { 43 | throw "Path to run command does not exist: '$path'" 44 | } 45 | 46 | Push-Location $path 47 | } 48 | 49 | # determine which prompt in which to run the command 50 | switch ($prompt.ToLower()) 51 | { 52 | 'cmd' 53 | { 54 | Write-Message 'Running command via Command Prompt.' 55 | Write-Information "Command: $command" 56 | cmd.exe /C $command 57 | if ($LASTEXITCODE -ne 0) 58 | { 59 | Pop-Location 60 | throw "Failed to run command: '$command'." 61 | } 62 | } 63 | 64 | 'powershell' 65 | { 66 | Write-Message 'Running command via PowerShell.' 67 | Write-Information "Command: $command" 68 | powershell.exe /C $command 69 | if (!$?) 70 | { 71 | Pop-Location 72 | throw "Failed to run command: '$command'." 73 | } 74 | } 75 | 76 | default 77 | { 78 | throw "Unrecognised prompt for command colour: '$prompt'." 79 | } 80 | } 81 | 82 | Pop-Location 83 | Write-Message 'Command ran successfully.' 84 | } 85 | 86 | function Test-Module($colour, $variables, $credentials) 87 | { 88 | # Check the command 89 | $command = Replace-Variables $colour.command $variables 90 | if ([string]::IsNullOrWhiteSpace($command)) 91 | { 92 | throw 'No command passed to run via Command Prompt.' 93 | } 94 | 95 | # Check the prompt type 96 | $prompt = Replace-Variables $colour.prompt $variables 97 | $prompts = @('cmd', 'powershell') 98 | if (![string]::IsNullOrWhiteSpace($prompt) -and $prompts -inotcontains ($prompt.Trim())) 99 | { 100 | throw ("Invalid prompt type found: '$prompt'. Can be only: {0}." -f ($prompts -join ', ')) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Modules/copy.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "copy", 15 | # "from": "C:\\path\\to\\folder", 16 | # "to": "C:\\path\\to\\other\\folder", 17 | # "excludeFiles": [ "*.html", "*.js" ], 18 | # "includeFolders": [ "src" ] 19 | # } 20 | # ] 21 | # } 22 | ######################################################################### 23 | 24 | # Copy files/folders from one location to another 25 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 26 | 27 | function Start-Module($colour, $variables, $credentials) 28 | { 29 | Test-Module $colour $variables $credentials 30 | 31 | $from = (Replace-Variables $colour.from $variables).Trim() 32 | $to = (Replace-Variables $colour.to $variables).Trim() 33 | 34 | if (!(Test-Path $from)) 35 | { 36 | throw "From path specified doesn't exist: '$from'." 37 | } 38 | 39 | $excludeFiles = $colour.excludeFiles 40 | $excludeFolders = $colour.excludeFolders 41 | 42 | if ($excludeFolders -ne $null -and $excludeFolders.Length -gt 0) 43 | { 44 | [Regex]$excludeFoldersRegex = (($excludeFolders | ForEach-Object {[Regex]::Escape((Replace-Variables $_ $variables))}) -join '|') 45 | } 46 | 47 | if ($excludeFiles -ne $null) 48 | { 49 | for ($i = 0; $i -lt $excludeFiles.Length; $i++) 50 | { 51 | $excludeFiles[$i] = Replace-Variables $excludeFiles[$i] $variables 52 | } 53 | } 54 | 55 | $includeFiles = $colour.includeFiles 56 | $includeFolders = $colour.includeFolders 57 | 58 | if ($includeFolders -ne $null -and $includeFolders.Length -gt 0) 59 | { 60 | [Regex]$includeFoldersRegex = (($includeFolders | ForEach-Object {[Regex]::Escape((Replace-Variables $_ $variables))}) -join '|') 61 | } 62 | 63 | if ($includeFiles -ne $null) 64 | { 65 | for ($i = 0; $i -lt $includeFiles.Length; $i++) 66 | { 67 | $includeFiles[$i] = Replace-Variables $includeFiles[$i] $variables 68 | } 69 | } 70 | 71 | Write-Message "Copying files/folders from '$from' to '$to'." 72 | 73 | Get-ChildItem -Path $from -Recurse -Force -Exclude $excludeFiles -Include $includeFiles | 74 | Where-Object { $excludeFoldersRegex -eq $null -or $_.FullName.Replace($from, [String]::Empty) -notmatch $excludeFoldersRegex } | 75 | Where-Object { $includeFoldersRegex -eq $null -or $_.FullName.Replace($from, [String]::Empty) -match $includeFoldersRegex } | 76 | Copy-Item -Destination { 77 | if ($_.PSIsContainer) 78 | { 79 | $path = Join-Path $to $_.Parent.FullName.Substring($from.Length) 80 | $temp = $path 81 | } 82 | else 83 | { 84 | $path = Join-Path $to $_.FullName.Substring($from.Length) 85 | $temp = Split-Path -Parent $path 86 | } 87 | 88 | if (!(Test-Path $temp)) 89 | { 90 | New-Item -ItemType Directory -Force -Path $temp | Out-Null 91 | } 92 | 93 | $path 94 | } -Force -Exclude $excludeFiles -Include $includeFiles 95 | 96 | if (!$?) 97 | { 98 | throw 'Failed to copy files/folders.' 99 | } 100 | 101 | Write-Message 'Files/folders copied successfully.' 102 | } 103 | 104 | function Test-Module($colour, $variables, $credentials) 105 | { 106 | $from = Replace-Variables $colour.from $variables 107 | if ([string]::IsNullOrWhiteSpace($from)) 108 | { 109 | throw 'No from path specified.' 110 | } 111 | 112 | $to = Replace-Variables $colour.to $variables 113 | if ([string]::IsNullOrWhiteSpace($to)) 114 | { 115 | throw 'No to path specified.' 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Modules/directory.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "directory", 15 | # "ensure": "exists", 16 | # "path": "C:\\path\\to\\some\\where\\to\\make" 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Creates or removes a directory 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | $path = (Replace-Variables $colour.path $variables).Trim() 30 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 31 | 32 | Write-Message "Ensuring '$path' $ensure." 33 | 34 | switch ($ensure) 35 | { 36 | 'exists' 37 | { 38 | if (!(Test-Path $path)) 39 | { 40 | New-Item -ItemType Directory -Path $path -Force | Out-Null 41 | if (!$?) { 42 | throw 'Failed to create directory path.' 43 | } 44 | } 45 | } 46 | 47 | 'removed' 48 | { 49 | if (Test-Path $path) 50 | { 51 | Remove-Item -Path $path -Force -Recurse | Out-Null 52 | if (!$?) 53 | { 54 | throw 'Failed to remove directory path.' 55 | } 56 | } 57 | } 58 | } 59 | 60 | Write-Message "'$path' $ensure." 61 | } 62 | 63 | function Test-Module($colour, $variables, $credentials) 64 | { 65 | $path = Replace-Variables $colour.path $variables 66 | if ([string]::IsNullOrWhiteSpace($path)) 67 | { 68 | throw 'No path passed.' 69 | } 70 | 71 | $ensure = Replace-Variables $colour.ensure $variables 72 | $ensures = @('exists', 'removed') 73 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 74 | { 75 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Modules/echo.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "echo", 15 | # "text": "Hello, world!" 16 | # } 17 | # ] 18 | # } 19 | ######################################################################### 20 | 21 | # Simple echo module to display whatever is written 22 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 23 | 24 | function Start-Module($colour, $variables, $credentials) 25 | { 26 | Test-Module $colour $variables $credentials 27 | 28 | $text = Replace-Variables $colour.text $variables 29 | Write-Host $text 30 | 31 | if (!$?) 32 | { 33 | throw "Failed to echo: '$text'." 34 | } 35 | } 36 | 37 | function Test-Module($colour, $variables, $credentials) 38 | { 39 | $text = Replace-Variables $colour.text $variables 40 | if ([string]::IsNullOrWhiteSpace($text)) 41 | { 42 | throw 'No text passed to echo.' 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Modules/edison.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "edison", 15 | # "fixtureThreads": 2, 16 | # "testThreads": 2, 17 | # "outputType": "json", 18 | # "url": "http://some.com/url", 19 | # "testId": "ID", 20 | # "exclude": [ 21 | # "SomeCategory" 22 | # ], 23 | # "include": [ 24 | # "SomeCategory2" 25 | # ], 26 | # "tests": [ 27 | # "Example\\Test1.dll", 28 | # "Example\\Test2.dll" 29 | # ] 30 | # } 31 | # ] 32 | # } 33 | ######################################################################### 34 | 35 | # Use Edison to run tests 36 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 37 | 38 | function Start-Module($colour, $variables, $credentials) 39 | { 40 | Test-Module $colour $variables $credentials 41 | 42 | if (!(Test-Software 'Edison.Console.exe --version' 'Edison')) 43 | { 44 | Write-Warnings 'Edison is not installed' 45 | Install-AdhocSoftware 'edison' 'Edison' 46 | } 47 | 48 | $tests = $colour.tests 49 | $excludes = $colour.exclude 50 | $includes = $colour.include 51 | 52 | $url = Replace-Variables $colour.url $variables 53 | $testId = Replace-Variables $colour.testId $variables 54 | 55 | $fixtureThreads = Replace-Variables $colour.fixtureThreads $variables 56 | if ([string]::IsNullOrWhiteSpace($fixtureThreads)) 57 | { 58 | $fixtureThreads = 1 59 | } 60 | 61 | $testThreads = Replace-Variables $colour.testThreads $variables 62 | if ([string]::IsNullOrWhiteSpace($testThreads)) 63 | { 64 | $testThreads = 1 65 | } 66 | 67 | $outputType = Replace-Variables $colour.outputType $variables 68 | if ([string]::IsNullOrWhiteSpace($outputType)) 69 | { 70 | $outputType = 'json' 71 | } 72 | 73 | # Join the tests 74 | ForEach ($test in $tests) 75 | { 76 | $test = (Replace-Variables $test $variables).Trim() 77 | 78 | if (!(Test-Path $test)) 79 | { 80 | throw "Path to test does not exist: '$test'." 81 | } 82 | } 83 | 84 | $test_string = Replace-Variables ($tests -join ', ') $variables 85 | 86 | # Setup the final argument string 87 | $final_args = "--a $test_string --cot dot --ft $fixtureThreads --tt $testThreads --ot $outputType" 88 | 89 | # Add the test URL if we have one 90 | if (![string]::IsNullOrWhiteSpace($url)) 91 | { 92 | $final_args += " --url $url --tid $testId" 93 | } 94 | 95 | # If we have any excluded categories, add them 96 | if ($excludes -ne $null -and $excludes.Length -gt 0) 97 | { 98 | $exclude_string = Replace-Variables ($excludes -join ', ') $variables 99 | $final_args += " --exclude $exclude_string" 100 | } 101 | 102 | # If we have any included categories, add them 103 | if ($includes -ne $null -and $includes.Length -gt 0) 104 | { 105 | $include_string = Replace-Variables ($includes -join ', ') $variables 106 | $final_args += " --include $include_string" 107 | } 108 | 109 | Write-Message 'Running tests.' 110 | Run-Command 'Edison.Console.exe' "$final_args" 111 | Write-Message 'Tests ran successfully.' 112 | } 113 | 114 | function Test-Module($colour, $variables, $credentials) 115 | { 116 | # Check the specified tests 117 | $tests = $colour.tests 118 | if ($tests -eq $null -or $tests.Length -eq 0) 119 | { 120 | throw 'No tests have been supplied for Edison.' 121 | } 122 | 123 | ForEach ($test in $tests) 124 | { 125 | $test = Replace-Variables $test $variables 126 | 127 | if ([string]::IsNullOrWhiteSpace($test)) 128 | { 129 | throw 'No path specified for tests.' 130 | } 131 | } 132 | 133 | # Check the categoies if any have been supplied 134 | $excludes = $colour.exclude 135 | if ($excludes -ne $null -and $excludes.Length -gt 0) 136 | { 137 | ForEach ($exclude in $excludes) 138 | { 139 | $exclude = Replace-Variables $exclude $variables 140 | 141 | if ([string]::IsNullOrWhiteSpace($exclude)) 142 | { 143 | throw 'Excluded category cannot be empty.' 144 | } 145 | } 146 | } 147 | 148 | $includes = $colour.include 149 | if ($includes -ne $null -and $includes.Length -gt 0) 150 | { 151 | ForEach ($include in $includes) 152 | { 153 | $include = Replace-Variables $include $variables 154 | 155 | if ([string]::IsNullOrWhiteSpace($include)) 156 | { 157 | throw 'Included category cannot be empty.' 158 | } 159 | } 160 | } 161 | 162 | # Check thread values aren't negative or 0 163 | $fixtureThreads = Replace-Variables $colour.fixtureThreads $variables 164 | if (![string]::IsNullOrWhiteSpace($fixtureThreads) -and $fixtureThreads -lt 0) 165 | { 166 | throw "Fixture thread value must be greater than 0, but got '$fixtureThreads'." 167 | } 168 | 169 | $testThreads = Replace-Variables $colour.testThreads $variables 170 | if (![string]::IsNullOrWhiteSpace($testThreads) -and $testThreads -lt 0) 171 | { 172 | throw "Test thread value must be greater than 0, but got '$testThreads'." 173 | } 174 | 175 | # Check output type 176 | $outputType = Replace-Variables $colour.outputType $variables 177 | $types = @('json', 'xml', 'csv', 'dot', 'none') 178 | if (![string]::IsNullOrWhiteSpace($outputType) -and $types -inotcontains ($outputType.Trim())) 179 | { 180 | throw ("Invalid output type found: '$outputType'. Can be only: {0}." -f ($types -join ', ')) 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /src/Modules/email.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "email", 15 | # "host": "SMTPSERVER1", 16 | # "port": 25, 17 | # "subject": "This is an example email", 18 | # "body": "Hello, world!", 19 | # "attachments": [ 20 | # "C:\\path\\to\\some\\file.png" 21 | # ] 22 | # "from": "some@email.com", 23 | # "to": [ 24 | # "other@email.com" 25 | # ], 26 | # "cc": [ 27 | # "another@email.com" 28 | # ], 29 | # "bcc": [ 30 | # "example@email.com" 31 | # ], 32 | # "priority": "Normal", 33 | # "useSsl": false 34 | # } 35 | # ] 36 | # } 37 | ######################################################################### 38 | 39 | # Sends an email to some specified emails 40 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 41 | 42 | function Start-Module($colour, $variables, $credentials) 43 | { 44 | Test-Module $colour $variables $credentials 45 | 46 | $smtpServer = (Replace-Variables $colour.host $variables).Trim() 47 | $subject = (Replace-Variables $colour.subject $variables).Trim() 48 | $from = (Replace-Variables $colour.from $variables).Trim() 49 | 50 | $body = Replace-Variables $colour.body $variables 51 | if ($body -eq $null) 52 | { 53 | $body = [string]::Empty 54 | } 55 | 56 | $port = Replace-Variables $colour.port $variables 57 | if ([string]::IsNullOrWhiteSpace($port)) 58 | { 59 | $port = 25 60 | } 61 | 62 | $priority = Replace-Variables $colour.priority $variables 63 | if ([string]::IsNullOrWhiteSpace($priority)) 64 | { 65 | $priority = 'Normal' 66 | } 67 | 68 | $useSsl = Replace-Variables $colour.useSsl $variables 69 | if ([string]::IsNullOrWhiteSpace($useSsl)) 70 | { 71 | $useSsl = $false 72 | } 73 | 74 | $attachments = $colour.attachments 75 | $final_attachments = @() 76 | if ($attachments -ne $null) 77 | { 78 | ForEach ($attachment in $attachments) 79 | { 80 | $attachment = (Replace-Variables $attachment $variables).Trim() 81 | 82 | if (!(Test-Path $attachment)) 83 | { 84 | throw "Path to attachment for sending email does not exist: '$attachment'." 85 | } 86 | 87 | $final_attachments += $attachment 88 | } 89 | } 90 | 91 | $to_emails = $colour.to 92 | $final_to = @() 93 | if ($to_emails -ne $null) 94 | { 95 | ForEach ($to_email in $to_emails) 96 | { 97 | $to_email = (Replace-Variables $to_email $variables).Trim() 98 | $final_to += $to_email 99 | } 100 | } 101 | 102 | $cc_emails = $colour.cc 103 | $final_cc = @() 104 | if ($cc_emails -ne $null) 105 | { 106 | ForEach ($cc_email in $cc_emails) 107 | { 108 | $cc_email = (Replace-Variables $cc_email $variables).Trim() 109 | $final_cc += $cc_email 110 | } 111 | } 112 | 113 | $bcc_emails = $colour.bcc 114 | $final_bcc = @() 115 | if ($bcc_emails -ne $null) 116 | { 117 | ForEach ($bcc_email in $bcc_emails) 118 | { 119 | $bcc_email = (Replace-Variables $bcc_email $variables).Trim() 120 | $final_bcc += $bcc_email 121 | } 122 | } 123 | 124 | Write-Message 'Sending Email.' 125 | 126 | if ($useSsl) 127 | { 128 | Send-MailMessage -From $from -To $final_to -Cc $final_cc -Bcc $final_bcc -Attachments $final_attachments -Subject $subject -Body $body -BodyAsHtml -SmtpServer $smtpServer -Port $port -Priority $priority -UseSsl 129 | } 130 | else 131 | { 132 | Send-MailMessage -From $from -To $final_to -Cc $final_cc -Bcc $final_bcc -Attachments $final_attachments -Subject $subject -Body $body -BodyAsHtml -SmtpServer $smtpServer -Port $port -Priority $priority 133 | } 134 | 135 | if (!$?) 136 | { 137 | throw 'Failed to send email.' 138 | } 139 | 140 | Write-Message 'Sending email was successful.' 141 | } 142 | 143 | function Test-Module($colour, $variables, $credentials) 144 | { 145 | $smtpServer = Replace-Variables $colour.host $variables 146 | if ([string]::IsNullOrWhiteSpace($smtpServer)) 147 | { 148 | throw 'No host server specified from which to send the email.' 149 | } 150 | 151 | $subject = Replace-Variables $colour.subject $variables 152 | if ([string]::IsNullOrWhiteSpace($subject)) 153 | { 154 | throw 'No subject for the email specified.' 155 | } 156 | 157 | $from = Replace-Variables $colour.from $variables 158 | if ([string]::IsNullOrWhiteSpace($from)) 159 | { 160 | throw 'No from email address specified.' 161 | } 162 | 163 | $to_emails = $colour.to 164 | if ($to_emails -eq $null -or $to_emails.Length -eq 0) 165 | { 166 | throw 'No to email addresses specified.' 167 | } 168 | 169 | ForEach ($to_email in $to_emails) 170 | { 171 | $to_email = Replace-Variables $to_email $variables 172 | 173 | if ([string]::IsNullOrWhiteSpace($to_email)) 174 | { 175 | throw 'Cannot pass an empty to email address.' 176 | } 177 | } 178 | 179 | $cc_emails = $colour.cc 180 | if ($cc_emails -ne $null -and $cc_emails.Length -gt 0) 181 | { 182 | ForEach ($cc_email in $cc_emails) 183 | { 184 | $cc_email = Replace-Variables $cc_email $variables 185 | 186 | if ([string]::IsNullOrWhiteSpace($cc_email)) 187 | { 188 | throw 'Cannot pass an empty cc email address.' 189 | } 190 | } 191 | } 192 | 193 | $bcc_emails = $colour.bcc 194 | if ($bcc_emails -ne $null -and $bcc_emails.Length -gt 0) 195 | { 196 | ForEach ($bcc_email in $bcc_emails) 197 | { 198 | $bcc_email = Replace-Variables $bcc_email $variables 199 | 200 | if ([string]::IsNullOrWhiteSpace($bcc_email)) 201 | { 202 | throw 'Cannot pass an empty bcc email address.' 203 | } 204 | } 205 | } 206 | 207 | $priority = Replace-Variables $colour.priority $variables 208 | $priorities = @('normal', 'high', 'low') 209 | if (![string]::IsNullOrWhiteSpace($priority) -and $priorities -inotcontains $priority) 210 | { 211 | throw ("Invalid priority found: '$priority'. Can be only: {0}." -f ($priorities -join ', ')) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/Modules/git.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "git", 15 | # "remote": "https://path//to/some/branch.git" 16 | # "branch": "master", 17 | # "path": "C:\\path\\to\\place", 18 | # "name": "NewBranch" 19 | # } 20 | # ] 21 | # } 22 | ######################################################################### 23 | 24 | # Clones the remote repository into the supplied local path 25 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 26 | 27 | function Start-Module($colour, $variables, $credentials) 28 | { 29 | Test-Module $colour $variables $credentials 30 | 31 | # Check to see if git is installed, if not then install it 32 | if (!(Test-Software 'git --version' 'git')) 33 | { 34 | Write-Warnings 'Git is not installed' 35 | Install-AdhocSoftware 'git.install' 'Git' 36 | } 37 | 38 | $remote = (Replace-Variables $colour.remote $variables).Trim() 39 | $pattern = Get-GitPattern 40 | 41 | if (!($remote -imatch $pattern)) 42 | { 43 | throw "Remote git repository of '$remote' is not valid." 44 | } 45 | 46 | $directory = $matches['repo'] 47 | 48 | $path = (Replace-Variables $colour.path $variables).Trim() 49 | if (!(Test-Path $path)) 50 | { 51 | New-Item -ItemType Directory -Force -Path $path | Out-Null 52 | } 53 | 54 | $branch = Replace-Variables $colour.branch $variables 55 | if ([string]::IsNullOrWhiteSpace($branch)) 56 | { 57 | $branch = 'master' 58 | } 59 | else 60 | { 61 | $branch = $branch.Trim() 62 | } 63 | 64 | $commit = Replace-Variables $colour.commit $variables 65 | if ($commit -ne $null) 66 | { 67 | $commit = $commit.Trim() 68 | } 69 | 70 | $name = Replace-Variables $colour.name $variables 71 | if ($name -ne $null) 72 | { 73 | $name = $name.Trim() 74 | } 75 | 76 | # delete directory if exists 77 | Push-Location $path 78 | 79 | try 80 | { 81 | if ((Test-Path $directory)) 82 | { 83 | Backup-Directory $directory 84 | } 85 | elseif (![string]::IsNullOrWhiteSpace($name) -and (Test-Path $name)) 86 | { 87 | Backup-Directory $name 88 | } 89 | 90 | # clone 91 | Write-Message "Cloning git repository from '$remote' to '$path'." 92 | Run-Command 'git.exe' "clone $remote" 93 | 94 | # rename 95 | if (![string]::IsNullOrWhiteSpace($name)) 96 | { 97 | Rename-Item $directory $name | Out-Null 98 | if (!$?) 99 | { 100 | throw "Rename of directory from '$directory' to '$name' failed." 101 | } 102 | 103 | Write-Message "Local directory renamed from '$directory' to '$name'." 104 | $directory = $name 105 | } 106 | 107 | # checkout 108 | Write-Message "Checking out the '$branch' branch." 109 | Push-Location $directory 110 | 111 | try 112 | { 113 | Run-Command 'git.exe' "checkout $branch" 114 | 115 | # reset 116 | if (![string]::IsNullOrWhiteSpace($commit)) 117 | { 118 | Write-Message "Resetting local repository to the $commit commit." 119 | Run-Command 'git.exe' "reset --hard $commit" 120 | } 121 | 122 | Write-Message 'Git clone was successful.' 123 | } 124 | finally 125 | { 126 | Pop-Location 127 | } 128 | } 129 | finally 130 | { 131 | Pop-Location 132 | } 133 | } 134 | 135 | function Test-Module($colour, $variables, $credentials) 136 | { 137 | $remote = Replace-Variables $colour.remote $variables 138 | $pattern = Get-GitPattern 139 | 140 | if ([string]::IsNullOrWhiteSpace($remote) -or $remote.Trim() -inotmatch $pattern) 141 | { 142 | throw "Remote git repository of '$remote' is not valid." 143 | } 144 | 145 | $path = Replace-Variables $colour.path $variables 146 | if ([string]::IsNullOrWhiteSpace($path)) 147 | { 148 | throw 'No local git repository path specified.' 149 | } 150 | } 151 | 152 | function Get-GitPattern() 153 | { 154 | return '(\\|\/)(?[a-zA-Z]+)\.git' 155 | } 156 | -------------------------------------------------------------------------------- /src/Modules/hosts.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "hosts", 15 | # "ensure": "add", 16 | # "ip": "127.0.0.3", 17 | # "hostname": "test.local.com" 18 | # } 19 | # ] 20 | # } 21 | ######################################################################### 22 | 23 | # Updates the hosts file 24 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 25 | 26 | function Start-Module($colour, $variables, $credentials) 27 | { 28 | Test-Module $colour $variables $credentials 29 | 30 | $hostFile = "$env:windir\System32\drivers\etc\hosts" 31 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 32 | 33 | # check IP 34 | $ip = Replace-Variables $colour.ip $variables 35 | if ([String]::IsNullOrWhiteSpace($ip)) 36 | { 37 | $ip = [String]::Empty 38 | } 39 | else 40 | { 41 | $ip = $ip.Trim() 42 | } 43 | 44 | # check hostname 45 | $hostname = Replace-Variables $colour.hostname $variables 46 | if ([String]::IsNullOrWhiteSpace($hostname)) 47 | { 48 | $hostname = [String]::Empty 49 | } 50 | else 51 | { 52 | $hostname = $hostname.Trim() 53 | } 54 | 55 | Write-Message "Attempting to $ensure '$ip - $hostname'." 56 | $regex = ".*?$ip.*?$hostname.*?" 57 | $lines = Get-Content $hostFile 58 | 59 | switch ($ensure) 60 | { 61 | 'add' 62 | { 63 | $current = ($lines | Where-Object { $_ -match $regex } | Measure-Object).Count 64 | 65 | if ($current -eq 0) 66 | { 67 | ("`n$ip`t`t$hostname") | Out-File -FilePath $hostFile -Encoding ASCII -Append 68 | } 69 | else 70 | { 71 | Write-Message 'Host entry already exists.' 72 | } 73 | } 74 | 75 | 'remove' 76 | { 77 | $lines | Where-Object { $_ -notmatch $regex } | Out-File -FilePath $hostFile -Encoding ASCII 78 | } 79 | } 80 | 81 | if (!$?) 82 | { 83 | throw "Failed to $ensure '$ip - $hostname' to the hosts file." 84 | } 85 | 86 | Write-Message "$ensure of '$ip - $hostname' successful." 87 | } 88 | 89 | function Test-Module($colour, $variables, $credentials) 90 | { 91 | $hostFile = "$env:windir\System32\drivers\etc\hosts" 92 | if (!(Test-Path $hostFile)) 93 | { 94 | throw "Hosts file does not exist at: '$hostFile'." 95 | } 96 | 97 | $ensure = Replace-Variables $colour.ensure $variables 98 | $ensures = @('add', 'remove') 99 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 100 | { 101 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 102 | } 103 | 104 | # check IP 105 | $ip = Replace-Variables $colour.ip $variables 106 | if ([String]::IsNullOrWhiteSpace($ip)) 107 | { 108 | $ip = [String]::Empty 109 | } 110 | 111 | # check hostname 112 | $hostname = Replace-Variables $colour.hostname $variables 113 | if ([String]::IsNullOrWhiteSpace($hostname)) 114 | { 115 | $hostname = [String]::Empty 116 | } 117 | 118 | if ($ensure -eq 'add' -and ([String]::IsNullOrWhiteSpace($ip) -or [String]::IsNullOrWhiteSpace($hostname))) 119 | { 120 | throw 'No IP or Hostname has been supplied for adding a host entry.' 121 | } 122 | elseif ($ensure -eq 'remove' -and [String]::IsNullOrWhiteSpace($ip) -and [String]::IsNullOrWhiteSpace($hostname)) 123 | { 124 | throw 'No IP and Hostname have been supplied for removing a host entry.' 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/Modules/iis-app-pool.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "iis-app-pool", 15 | # "ensure": "added", 16 | # "state": "started", 17 | # "appPoolName": "Example Site" 18 | # } 19 | # ] 20 | # } 21 | ######################################################################### 22 | 23 | # Add/removes an application pool on IIS 24 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 25 | Import-Module WebAdministration -ErrorAction Stop 26 | sleep 2 27 | 28 | function Start-Module($colour, $variables, $credentials) 29 | { 30 | Test-Module $colour $variables $credentials 31 | 32 | $appPoolName = (Replace-Variables $colour.appPoolName $variables).Trim() 33 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 34 | $state = Replace-Variables $colour.state $variables 35 | 36 | $poolExists = (Test-Path "IIS:\AppPools\$appPoolName") 37 | 38 | switch ($ensure) 39 | { 40 | 'added' 41 | { 42 | if (!$poolExists) 43 | { 44 | throw "Application pool in IIS for updating does not exist: '$appPoolName'." 45 | } 46 | 47 | $state = $state.ToLower().Trim() 48 | Write-Message "`nEnsuring Application Pool is $state." 49 | 50 | switch ($state) 51 | { 52 | 'started' 53 | { 54 | Restart-WebAppPool -Name $appPoolName 55 | if (!$?) 56 | { 57 | throw 58 | } 59 | } 60 | 61 | 'stopped' 62 | { 63 | Stop-WebAppPool -Name $appPoolName 64 | if (!$?) 65 | { 66 | throw 67 | } 68 | } 69 | } 70 | 71 | Write-Message "Application Pool has been $state." 72 | } 73 | 74 | 'removed' 75 | { 76 | Write-Message "`nRemoving application pool: '$appPoolName'." 77 | 78 | if ($poolExists) 79 | { 80 | Remove-WebAppPool -Name $appPoolName 81 | if (!$?) 82 | { 83 | throw 84 | } 85 | } 86 | else 87 | { 88 | Write-Warnings 'Application pool does not exist.' 89 | } 90 | 91 | Write-Message 'Application pool removed successfully.' 92 | } 93 | } 94 | } 95 | 96 | function Test-Module($colour, $variables, $credentials) 97 | { 98 | if (!(Test-Win64)) 99 | { 100 | throw 'Shell needs to be running as a 64-bit host when setting up IIS websites.' 101 | } 102 | 103 | $appPoolName = Replace-Variables $colour.appPoolName $variables 104 | if ([string]::IsNullOrEmpty($appPoolName)) 105 | { 106 | throw 'No app pool name has been supplied.' 107 | } 108 | 109 | $appPoolName = $appPoolName.Trim() 110 | $poolExists = (Test-Path "IIS:\AppPools\$appPoolName") 111 | 112 | $ensure = Replace-Variables $colour.ensure $variables 113 | if ([string]::IsNullOrWhiteSpace($ensure)) 114 | { 115 | throw 'No ensure parameter supplied.' 116 | } 117 | 118 | # check we have a valid ensure property 119 | $ensure = $ensure.ToLower().Trim() 120 | if ($ensure -ne 'added' -and $ensure -ne 'removed') 121 | { 122 | throw "Invalid ensure parameter supplied: '$ensure'." 123 | } 124 | 125 | $state = Replace-Variables $colour.state $variables 126 | if ([string]::IsNullOrWhiteSpace($state) -and $ensure -eq 'added') 127 | { 128 | throw 'No state parameter supplied.' 129 | } 130 | 131 | # check we have a valid state property 132 | if ($state -ne $null) 133 | { 134 | $state = $state.ToLower().Trim() 135 | } 136 | 137 | if ($state -ne 'started' -and $state -ne 'stopped' -and $ensure -eq 'added') 138 | { 139 | throw "Invalid state parameter supplied: '$state'." 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Modules/iis-binding.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "iis-binding", 15 | # "ensure": "added", 16 | # "siteName": "Example Site", 17 | # "ip": "127.0.0.3", 18 | # "port": 443, 19 | # "protocol": "https", 20 | # "certificate": "\*.local.com" 21 | # } 22 | # ] 23 | # } 24 | ######################################################################### 25 | 26 | # Adds/removes a binding on a website via IIS 27 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 28 | Import-Module WebAdministration -ErrorAction Stop 29 | sleep 2 30 | 31 | function Start-Module($colour, $variables, $credentials) 32 | { 33 | Test-Module $colour $variables $credentials 34 | 35 | $siteName = (Replace-Variables $colour.siteName $variables).Trim() 36 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 37 | $protocol = (Replace-Variables $colour.protocol $variables).ToLower().Trim() 38 | 39 | $ip = (Replace-Variables $colour.ip $variables).Trim() 40 | $port = (Replace-Variables $colour.port $variables).Trim() 41 | $binding = ("*$ip" + ":" + "$port*") 42 | 43 | $siteExists = (Test-Path "IIS:\Sites\$siteName") 44 | 45 | if (!$siteExists) 46 | { 47 | throw "Website does not exist in IIS: '$siteName'." 48 | } 49 | 50 | $web = Get-Item "IIS:\Sites\$siteName" 51 | if (!$?) 52 | { 53 | throw 54 | } 55 | 56 | $col = $web.Bindings.Collection | Where-Object { $_.protocol -eq $protocol } 57 | 58 | switch ($ensure) 59 | { 60 | 'added' 61 | { 62 | Write-Message "Setting up website $protocol binding for '$ip" + ":" + "$port'." 63 | 64 | if ($col -eq $null -or $col.Length -eq 0 -or $col.bindingInformation -notlike $binding) 65 | { 66 | New-WebBinding -Name $siteName -IPAddress $ip -Port $port -Protocol $protocol 67 | if (!$?) 68 | { 69 | throw 70 | } 71 | 72 | if ($protocol -eq 'https') 73 | { 74 | $certificate = (Replace-Variables $colour.certificate $variables).Trim() 75 | $certs = (Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -like $certificate } | Select-Object -First 1) 76 | 77 | if ([string]::IsNullOrWhiteSpace($certs)) 78 | { 79 | throw "Certificate passed cannot be found when setting up website binding: '$certificate'." 80 | } 81 | 82 | $thumb = $certs.Thumbprint.ToString() 83 | 84 | $sslBindingsPath = 'hklm:\SYSTEM\CurrentControlSet\services\HTTP\Parameters\SslBindingInfo\' 85 | $registryItems = Get-ChildItem -Path $sslBindingsPath | Where-Object -FilterScript { $_.Property -eq 'DefaultSslCtlStoreName' } 86 | 87 | If ($registryItems.Count -gt 0) 88 | { 89 | ForEach ($item in $registryItems) 90 | { 91 | $item | Remove-ItemProperty -Name DefaultSslCtlStoreName 92 | Write-Host "Deleted DefaultSslCtlStoreName in " $item.Name 93 | } 94 | } 95 | 96 | Push-Location IIS:\SslBindings 97 | 98 | try 99 | { 100 | Get-Item Cert:\LocalMachine\My\$thumb | New-Item $ip!$port -Force | Out-Null 101 | if (!$?) 102 | { 103 | throw "Failed to create new SSL binding for $ip on $port" 104 | } 105 | } 106 | finally 107 | { 108 | Pop-Location 109 | } 110 | } 111 | } 112 | else 113 | { 114 | Write-Message 'Binding already exists.' 115 | } 116 | 117 | Write-Message 'Website binding setup successfully.' 118 | } 119 | 120 | 'removed' 121 | { 122 | Write-Message "Removing website $protocol binding for '$ip" + ":" + "$port'." 123 | 124 | if ($col -ne $null -and $col.Length -gt 0 -and $colour.bindingInformation -like $binding) { 125 | Remove-WebBinding -Name $siteName -IPAddress $ip -Port $port -Protocol $protocol 126 | if (!$?) 127 | { 128 | throw 129 | } 130 | 131 | if ($protocol -eq 'https') 132 | { 133 | Push-Location IIS:\SslBindings 134 | 135 | try 136 | { 137 | Remove-Item $ip!$port -Force 138 | if (!$?) 139 | { 140 | throw "Failed to remove SSL binding for $ip on $port" 141 | } 142 | } 143 | finally 144 | { 145 | Pop-Location 146 | } 147 | } 148 | } 149 | 150 | Write-Message 'Website binding removed successfully.' 151 | } 152 | } 153 | } 154 | 155 | function Test-Module($colour, $variables, $credentials) 156 | { 157 | if (!(Test-Win64)) 158 | { 159 | throw 'Shell needs to be running as a 64-bit host when setting up IIS website bindings.' 160 | } 161 | 162 | $siteName = Replace-Variables $colour.siteName $variables 163 | if ([string]::IsNullOrEmpty($siteName)) 164 | { 165 | throw 'No site name has been supplied for website.' 166 | } 167 | 168 | $ensure = Replace-Variables $colour.ensure $variables 169 | if ([string]::IsNullOrWhiteSpace($ensure)) 170 | { 171 | throw 'No ensure parameter supplied for website binding.' 172 | } 173 | 174 | # check we have a valid ensure property 175 | $ensure = $ensure.ToLower().Trim() 176 | if ($ensure -ne 'added' -and $ensure -ne 'removed') 177 | { 178 | throw "Invalid ensure supplied for website binding: '$ensure'." 179 | } 180 | 181 | $ip = Replace-Variables $colour.ip $variables 182 | if([string]::IsNullOrWhiteSpace($ip)) 183 | { 184 | throw 'No IP address passed to add website binding.' 185 | } 186 | 187 | $port = Replace-Variables $colour.port $variables 188 | if([string]::IsNullOrWhiteSpace($port)) 189 | { 190 | throw 'No port number passed to add website binding.' 191 | } 192 | 193 | $protocol = Replace-Variables $colour.protocol $variables 194 | if ([string]::IsNullOrWhiteSpace($protocol)) 195 | { 196 | throw 'No protocol passed for adding website binding.' 197 | } 198 | 199 | $protocol = $protocol.ToLower().Trim() 200 | if ($protocol -ne 'http' -and $protocol -ne 'https') 201 | { 202 | throw "Protocol for website binding is not valid. Expected http(s) but got: '$protocol'." 203 | } 204 | 205 | if ($ensure -eq 'added') 206 | { 207 | if ($protocol -eq 'https') 208 | { 209 | $certificate = Replace-Variables $colour.certificate $variables 210 | if ([string]::IsNullOrWhiteSpace($certificate)) 211 | { 212 | throw 'No certificate passed for setting up website binding https protocol.' 213 | } 214 | } 215 | 216 | $binding = ("*$ip" + ":" + "$port*") 217 | 218 | ForEach ($site in (Get-ChildItem IIS:\Sites)) 219 | { 220 | if ($site.Name -eq $siteName) 221 | { 222 | continue 223 | } 224 | 225 | $col = $site.Bindings.Collection | Where-Object { $_.protocol -eq $protocol } 226 | 227 | if ($col -eq $null -or $col.Length -eq 0) 228 | { 229 | continue 230 | } 231 | 232 | if ($col.bindingInformation -like $binding) 233 | { 234 | throw "Website already exists that uses $binding : '$($site.Name)'." 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/Modules/iis-website.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "iis", 15 | # "ensure": "updated", 16 | # "state": "started", 17 | # "siteName": "Example Site" 18 | # } 19 | # ] 20 | # } 21 | ######################################################################### 22 | 23 | # Add/removes a website on IIS 24 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 25 | Import-Module WebAdministration -ErrorAction Stop 26 | sleep 2 27 | 28 | function Start-Module($colour, $variables, $credentials) 29 | { 30 | Test-Module $colour $variables $credentials 31 | 32 | $siteName = (Replace-Variables $colour.siteName $variables).Trim() 33 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 34 | $state = Replace-Variables $colour.state $variables 35 | 36 | $siteExists = (Test-Path "IIS:\Sites\$siteName") 37 | 38 | switch ($ensure) 39 | { 40 | 'updated' 41 | { 42 | if (!$siteExists) 43 | { 44 | throw "Site for IIS website updating does not exist: '$siteName'." 45 | } 46 | 47 | $state = $state.ToLower().Trim() 48 | Write-Message "`nEnsuring Website is $state." 49 | 50 | switch ($state) 51 | { 52 | 'started' 53 | { 54 | $pool = (Get-Item "IIS:\Sites\$siteName" | Select-Object applicationPool).applicationPool 55 | if ($pool -ne $null) 56 | { 57 | Restart-WebAppPool -Name $pool 58 | if (!$?) 59 | { 60 | throw 61 | } 62 | } 63 | 64 | Start-Website -Name $siteName 65 | if (!$?) 66 | { 67 | throw 68 | } 69 | } 70 | 71 | 'stopped' 72 | { 73 | Stop-Website -Name $siteName 74 | if (!$?) 75 | { 76 | throw 77 | } 78 | } 79 | } 80 | 81 | Write-Message "Website has been $state." 82 | } 83 | 84 | 'removed' 85 | { 86 | Write-Message "Removing website: '$siteName'." 87 | 88 | if ($siteExists) 89 | { 90 | Remove-Website -Name $siteName 91 | if (!$?) 92 | { 93 | throw 94 | } 95 | } 96 | else 97 | { 98 | Write-Warnings 'Website does not exist.' 99 | } 100 | 101 | Write-Message 'Website removed successfully.' 102 | } 103 | } 104 | } 105 | 106 | function Test-Module($colour, $variables, $credentials) 107 | { 108 | if (!(Test-Win64)) 109 | { 110 | throw 'Shell needs to be running as a 64-bit host when setting up IIS websites.' 111 | } 112 | 113 | $siteName = Replace-Variables $colour.siteName $variables 114 | if ([string]::IsNullOrEmpty($siteName)) 115 | { 116 | throw 'No site name has been supplied for website.' 117 | } 118 | 119 | $siteName = $siteName.Trim() 120 | $siteExists = (Test-Path "IIS:\Sites\$siteName") 121 | 122 | $ensure = Replace-Variables $colour.ensure $variables 123 | if ([string]::IsNullOrWhiteSpace($ensure)) 124 | { 125 | throw 'No ensure parameter supplied for website.' 126 | } 127 | 128 | # check we have a valid ensure property 129 | $ensure = $ensure.ToLower().Trim() 130 | if ($ensure -ne 'added' -and $ensure -ne 'removed') 131 | { 132 | throw "Invalid ensure parameter supplied for website: '$ensure'." 133 | } 134 | 135 | $state = Replace-Variables $colour.state $variables 136 | if ([string]::IsNullOrWhiteSpace($state) -and $ensure -eq 'added') 137 | { 138 | throw 'No state parameter supplied for website.' 139 | } 140 | 141 | # check we have a valid state property 142 | if ($state -ne $null) 143 | { 144 | $state = $state.ToLower().Trim() 145 | } 146 | 147 | if ($state -ne 'started' -and $state -ne 'stopped' -and $ensure -eq 'added') 148 | { 149 | throw "Invalid state parameter supplied for website: '$state'." 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Modules/ms-sql.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "ms-sql", 15 | # "connectionString": "server=(local);Database=Example;Trusted_Connection=True;", 16 | # "sql": "INSERT INTO [SomeTable] VALUES ('value', 1001)", 17 | # "notIf": "SELECT TOP 1 * FROM [SomeTable] WHERE SomeValue = 'value'" 18 | # }, 19 | # { 20 | # "type": "ms-sql", 21 | # "connectionString": "server=(local);Database=Example;Trusted_Connection=True;", 22 | # "sqlFile": "C:\\path\\to\\some.sql", 23 | # "notIf": "SELECT TOP 1 * FROM [SomeTable] WHERE SomeValue = 'value'" 24 | # } 25 | # ] 26 | # } 27 | ######################################################################### 28 | 29 | # Execute the passed SQL against the passed connection string 30 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 31 | 32 | function Start-Module($colour, $variables, $credentials) 33 | { 34 | Test-Module $colour $variables $credentials 35 | 36 | $connectionString = (Replace-Variables $colour.connectionString $variables).Trim() 37 | $sql = Replace-Variables $colour.sql $variables 38 | $sqlFile = Replace-Variables $colour.sqlFile $variables 39 | $notIf = Replace-Variables $colour.notIf $variables 40 | 41 | try 42 | { 43 | # Attempt connection using connection string 44 | Write-Information "Opening Connection: $connectionString" 45 | $conn = [System.Data.SqlClient.SqlConnection] $connectionString 46 | $conn.Open() 47 | 48 | if (!$?) 49 | { 50 | throw "Failed to open connection to database using connection string supplied." 51 | } 52 | 53 | # Check if the notIf command is present 54 | if (![string]::IsNullOrWhiteSpace($notIf)) 55 | { 56 | Write-Message 'Checking database against notIf command.' 57 | $notIf = "IF EXISTS ($notIf) BEGIN SELECT 1 END ELSE BEGIN SELECT 0 END" 58 | $sqlCommand = $conn.CreateCommand() 59 | $sqlCommand.CommandText = $notIf 60 | $result = $sqlCommand.ExecuteScalar() 61 | 62 | if ($result -eq 1) 63 | { 64 | Write-Information 'The notIf command returned true, not running main SQL.' 65 | return 66 | } 67 | } 68 | 69 | Write-Message 'Running SQL command.' 70 | if ([string]::IsNullOrWhiteSpace($sql)) 71 | { 72 | $sql = Get-Content $sqlFile -Raw 73 | } 74 | 75 | $sqlCommand = $conn.CreateCommand() 76 | $sqlCommand.CommandText = $sql 77 | $sqlCommand.ExecuteNonQuery() 78 | 79 | if (!$?) 80 | { 81 | throw 'Running the SQL command failed.' 82 | } 83 | 84 | Write-Message 'SQL command ran successfully.' 85 | } 86 | finally 87 | { 88 | if ($conn -ne $null) 89 | { 90 | $conn.Close() 91 | } 92 | } 93 | } 94 | 95 | function Test-Module($colour, $variables, $credentials) 96 | { 97 | # Check the connection string 98 | $connectionString = Replace-Variables $colour.connectionString $variables 99 | if ([string]::IsNullOrWhiteSpace($connectionString)) 100 | { 101 | throw 'No connection string passed to connect to database.' 102 | } 103 | 104 | # Check the SQL/file command 105 | $sql = Replace-Variables $colour.sql $variables 106 | $sqlFile = Replace-Variables $colour.sqlFile $variables 107 | 108 | if ([string]::IsNullOrWhiteSpace($sql) -and [string]::IsNullOrWhiteSpace($sqlFile)) 109 | { 110 | throw 'No SQL or SQL file path passed to execute against the database.' 111 | } 112 | 113 | if (![string]::IsNullOrWhiteSpace($sql) -and ![string]::IsNullOrWhiteSpace($sqlFile)) 114 | { 115 | throw 'Cannot pass both SQL and a SQL file path.' 116 | } 117 | 118 | # Check the SQL file, if passed 119 | if (![string]::IsNullOrWhiteSpace($sqlFile) -and $variables['__initial_validation__'] -eq $false) 120 | { 121 | if (!(Test-Path $sqlFile)) 122 | { 123 | throw "The SQL file path passed does not exist: '$sqlFile'." 124 | } 125 | 126 | $sql = Get-Content $sqlFile -Raw 127 | if ([string]::IsNullOrWhiteSpace($sql)) 128 | { 129 | throw "There is no SQL within the passed SQL file: '$sqlFile'." 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Modules/msbuild.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "msbuild", 15 | # "toolpath": "C:\\path\\to\\msbuild.exe", 16 | # "configuration": "Debug", 17 | # "projects": [ 18 | # "C:\\path\\to\\project.csproj", 19 | # "C:\\path\\to\\solution.sln" 20 | # ], 21 | # "arguments": "/t:Rebuild", 22 | # "clean": false 23 | # } 24 | # ] 25 | # } 26 | ######################################################################### 27 | 28 | # Use MSBuild to build a project or solution 29 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 30 | 31 | function Start-Module($colour, $variables, $credentials) 32 | { 33 | Test-Module $colour $variables $credentials 34 | 35 | $toolpath = Replace-Variables $colour.toolpath $variables 36 | if ([string]::IsNullOrWhiteSpace($toolpath)) 37 | { 38 | Write-Message 'No MSBuild tool path supplied, using default.' 39 | $toolpath = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 40 | } 41 | else 42 | { 43 | $toolpath = $toolpath.Trim() 44 | } 45 | 46 | $configuration = Replace-Variables $colour.configuration $variables 47 | if ([string]::IsNullOrWhiteSpace($configuration)) 48 | { 49 | $configuration = 'Debug' 50 | } 51 | 52 | $projects = $colour.projects 53 | $clean = Replace-Variables $colour.clean $variables 54 | 55 | $_args = Replace-Variables $colour.arguments $variables 56 | if ([string]::IsNullOrWhiteSpace($_args)) 57 | { 58 | $_args = [string]::Empty 59 | } 60 | 61 | ForEach ($project in $projects) 62 | { 63 | $project = (Replace-Variables $project $variables).Trim() 64 | if (!(Test-Path $project)) 65 | { 66 | throw "Path to project for building does not exist: '$project'." 67 | } 68 | 69 | Push-Location (Split-Path $project -Parent) 70 | 71 | try 72 | { 73 | $file = (Split-Path $project -Leaf) 74 | 75 | Write-SubHeader "$file" 76 | Write-Information "Arguments: '$_args'." 77 | 78 | $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() 79 | 80 | if (![string]::IsNullOrWhiteSpace($clean) -and $clean -eq $true) 81 | { 82 | Write-Host 'Cleaning...' 83 | Build-Project $toolpath "/p:Configuration=$configuration /t:Clean $project" 84 | } 85 | 86 | Write-Host 'Building...' 87 | Build-Project $toolpath "/p:Configuration=$configuration $_args $project" 88 | 89 | Write-Stamp ('Time taken: {0}' -f $stopwatch.Elapsed) 90 | Write-Message "Project built successfully." 91 | Write-NewLine 92 | } 93 | finally 94 | { 95 | Pop-Location 96 | } 97 | } 98 | } 99 | 100 | function Test-Module($colour, $variables, $credentials) 101 | { 102 | $toolpath = Replace-Variables $colour.toolpath $variables 103 | if ([string]::IsNullOrWhiteSpace($toolpath)) 104 | { 105 | $toolpath = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe' 106 | } 107 | 108 | if (!(Test-Path ($toolpath.Trim()))) 109 | { 110 | throw "Invalid tool path to MSBuild.exe supplied: '$toolpath'." 111 | } 112 | 113 | $clean = Replace-Variables $colour.clean $variables 114 | if (![string]::IsNullOrWhiteSpace($clean) -and $clean -ne $true -and $clean -ne $false) 115 | { 116 | throw "Invalid value for clean: '$clean'. Should be either true or false." 117 | } 118 | 119 | $projects = $colour.projects 120 | if ($projects -eq $null -or $projects.Length -eq 0) 121 | { 122 | throw 'No projects have been supplied for MSBuild.' 123 | } 124 | 125 | ForEach ($project in $projects) 126 | { 127 | if ([string]::IsNullOrWhiteSpace($project)) 128 | { 129 | throw 'No path specified to build project.' 130 | } 131 | } 132 | } 133 | 134 | 135 | function Build-Project($command, $_args) 136 | { 137 | Run-Command $command $_args 138 | } 139 | -------------------------------------------------------------------------------- /src/Modules/net-share.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "net-share", 15 | # "ensure": "exists", 16 | # "name": "share.folder.name", 17 | # "path": "C:\\path\\to\\folder\\to\\share", 18 | # "remark": "Some Description", 19 | # "grants": { 20 | # "Everyone": "FULL" 21 | # } 22 | # }, 23 | # { 24 | # "type": "net-share", 25 | # "ensure": "removed", 26 | # "name": "share.folder.name" 27 | # } 28 | # ] 29 | # } 30 | ######################################################################### 31 | 32 | # Shares a supplied path, with given permissions 33 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 34 | Import-Module WebAdministration -ErrorAction Stop 35 | sleep 2 36 | 37 | function Start-Module($colour, $variables, $credentials) 38 | { 39 | Test-Module $colour $variables $credentials 40 | 41 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 42 | $name = (Replace-Variables $colour.name $variables).ToLower().Trim() 43 | 44 | Write-Message "`nEnsuring $name share $ensure." 45 | 46 | switch ($ensure) 47 | { 48 | 'exists' 49 | { 50 | $path = (Replace-Variables $colour.path $variables).Trim() 51 | 52 | if (!(Test-Path $path)) 53 | { 54 | throw "Path for sharing does not exist: '$path'." 55 | } 56 | 57 | Write-Information "Sharing path: '$path'." 58 | 59 | $remark = $colour.remark 60 | if ([string]::IsNullOrWhiteSpace($remark)) 61 | { 62 | $remark = 'Created via Picassio.' 63 | } 64 | 65 | $grants = $colour.grants 66 | $keys = $grants.psobject.properties.name 67 | $grantsArgs = [string]::Empty 68 | 69 | ForEach ($key in $keys) 70 | { 71 | # Grab the grant we're dealing with currently 72 | $key = (Replace-Variables $key $variables).Trim() 73 | 74 | # What permission access are we granting 75 | $permission = (Replace-Variables $grants.$key $variables).ToUpper().Trim() 76 | 77 | $grantsArgs += "/grant:`"$key,$permission`" " 78 | } 79 | 80 | try 81 | { 82 | Run-Command 'net' "share $name /delete /y" 83 | } 84 | catch 85 | { 86 | Write-Warnings "Failed to delete share $name before creating it. There's a high chance this error has occurred either due to permissions, or because the share just doesn't exist yet." 87 | } 88 | 89 | Run-Command 'net' "share $name=$path $grantsArgs /remark:$remark" 90 | } 91 | 92 | 'removed' 93 | { 94 | Run-Command 'net' "share $name /delete /y" 95 | } 96 | } 97 | 98 | Write-Message "The $name share $ensure." 99 | } 100 | 101 | function Test-Module($colour, $variables, $credentials) 102 | { 103 | # Check the ensures value 104 | $ensure = Replace-Variables $colour.ensure $variables 105 | $ensures = @('exists', 'removed') 106 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 107 | { 108 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 109 | } 110 | 111 | # Check we actually have a share name 112 | $name = Replace-Variables $colour.name $variables 113 | if ([string]::IsNullOrWhiteSpace($name)) 114 | { 115 | throw 'Share name cannot be empty.' 116 | } 117 | 118 | # If creating share, then check path and grants 119 | if ($ensure -ieq 'exists') 120 | { 121 | # Check path to folder for sharing was supplied 122 | $path = Replace-Variables $colour.path $variables 123 | if ([string]::IsNullOrWhiteSpace($path)) 124 | { 125 | throw 'No path to a folder to share passed.' 126 | } 127 | 128 | # Check we have grants values 129 | $grants = $colour.grants 130 | if ($grants -eq $null) 131 | { 132 | throw 'No grants have been supplied.' 133 | } 134 | 135 | # Grab the names of the grants, ensure we have valid values 136 | $keys = $grants.psobject.properties.name 137 | if ($keys -eq $null -or $keys.Length -eq 0) 138 | { 139 | throw 'No grant names have been supplied.' 140 | } 141 | 142 | $grantValues = @('read', 'change', 'full') 143 | if (($keys | Where-Object { $grantValues -inotcontains (Replace-Variables $_ $variables) } | Measure-Object).Count -gt 0) 144 | { 145 | throw 'Invalid or empty grant permission values found.' 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/Modules/nlb-node.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "nlb-node", 15 | # "ensure": "stop", 16 | # "drain": true, 17 | # "timeout": 30, 18 | # "nodes": [ 19 | # "NODE_NAME_1" 20 | # ] 21 | # }, 22 | # { 23 | # "type": "nlb-node", 24 | # "ensure": "start", 25 | # "nodes": [ 26 | # "NODE_NAME_1" 27 | # ] 28 | # }, 29 | # { 30 | # "type": "nlb-node", 31 | # "ensure": "add", 32 | # "cluster": "NODE_CLUSTER_NAME", 33 | # "interface": "vlan-3", 34 | # "nodes": [ 35 | # "NODE_NAME_1" 36 | # ] 37 | # } 38 | # ] 39 | # } 40 | ######################################################################### 41 | 42 | # Adds, removes, starts, stops nodes on Network Load Balancer 43 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 44 | Import-Module NetworkLoadBalancingClusters -ErrorAction Stop 45 | 46 | function Start-Module($colour, $variables, $credentials) 47 | { 48 | Test-Module $colour $variables $credentials 49 | 50 | $ensure = (Replace-Variables $colour.ensure $variables).Trim().ToLower() 51 | $nodes = $colour.nodes 52 | $drain = Replace-Variables $colour.drain $variables 53 | 54 | $timeout = Replace-Variables $colour.timeout $variables 55 | if ([string]::IsNullOrWhiteSpace($timeout)) 56 | { 57 | $timeout = 30 58 | } 59 | 60 | ForEach ($node in $nodes) 61 | { 62 | $node = (Replace-Variables $node $variables).Trim() 63 | 64 | Write-Message "Attempting to $ensure the $node node." 65 | 66 | switch ($ensure) 67 | { 68 | 'add' 69 | { 70 | $cluster = (Replace-Variables $colour.cluster $variables).Trim() 71 | $interface = (Replace-Variables $colour.interface $variables).Trim() 72 | Get-NlbCluster $cluster | Add-NlbClusterNode -NewNodeName $node -NewNodeInterface $interface -Force 73 | } 74 | 75 | 'remove' 76 | { 77 | Remove-NlbClusterNode $node -Force 78 | } 79 | 80 | 'start' 81 | { 82 | Start-NlbClusterNode $node 83 | } 84 | 85 | 'stop' 86 | { 87 | if ($drain -eq $true) 88 | { 89 | Stop-NlbClusterNode $node -Drain -Timeout $timeout 90 | } 91 | else 92 | { 93 | Stop-NlbClusterNode $node 94 | } 95 | } 96 | 97 | 'suspend' 98 | { 99 | Suspend-NlbClusterNode $node 100 | } 101 | 102 | 'resume' 103 | { 104 | Resume-NlbClusterNode $node 105 | } 106 | } 107 | 108 | if (!$?) 109 | { 110 | throw "Failed to $ensure the $node node." 111 | } 112 | 113 | Write-Message "$ensure of $node node successful." 114 | } 115 | } 116 | 117 | function Test-Module($colour, $variables, $credentials) 118 | { 119 | $ensure = Replace-Variables $colour.ensure $variables 120 | $ensures = @('stop', 'start', 'add', 'remove', 'resume', 'suspend') 121 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 122 | { 123 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 124 | } 125 | 126 | $nodes = $colour.nodes 127 | if ($nodes -eq $null -or $nodes.Length -eq 0) 128 | { 129 | throw 'No nodes to load balance specified.' 130 | } 131 | 132 | ForEach ($node in $nodes) 133 | { 134 | $node = Replace-Variables $node $variables 135 | 136 | if ([string]::IsNullOrWhiteSpace($node)) 137 | { 138 | throw 'Cannot pass an empty node name for load balancing.' 139 | } 140 | } 141 | 142 | if ($ensure -ieq 'add') 143 | { 144 | $cluster = Replace-Variables $colour.cluster $variables 145 | if ([string]::IsNullOrWhiteSpace($cluster)) 146 | { 147 | throw 'No cluster name specified when adding new node to load balance.' 148 | } 149 | 150 | $interface = Replace-Variables $colour.interface $variables 151 | if ([string]::IsNullOrWhiteSpace($interface)) 152 | { 153 | throw 'No interface type specified when adding new node to load balance.' 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/Modules/nodejs.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "nodejs", 15 | # "file": "C:\\path\\to\\app.js", 16 | # "npmInstall": true 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Opens a new Powershell host, and runs the node command on the passed file 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | if (!(Test-Software 'node.exe -v' 'nodejs')) 30 | { 31 | Write-Warnings 'Node.js is not installed' 32 | Install-AdhocSoftware 'nodejs.install' 'node.js' 33 | } 34 | 35 | $npm = Replace-Variables $colour.npmInstall $variables 36 | if (![string]::IsNullOrWhiteSpace($npm) -and $npm -eq $true) 37 | { 38 | if (!(Test-Software 'npm -v' 'npm')) 39 | { 40 | Write-Warnings 'npm is not installed' 41 | Install-AdhocSoftware 'npm' 'npm' 42 | } 43 | } 44 | 45 | $file = Replace-Variables $colour.file $variables 46 | if (!(Test-Path $file)) 47 | { 48 | throw "Path to file to run for node does not exist: '$file'" 49 | } 50 | 51 | Push-Location (Split-Path -Parent $file) 52 | 53 | if ($npm -eq $true) 54 | { 55 | Write-Information 'Installing npm modules.' 56 | npm install 57 | 58 | if (!$?) 59 | { 60 | Pop-Location 61 | throw "Failed to run npm install for: '$file'." 62 | } 63 | } 64 | 65 | $mainfile = (Split-Path -Leaf $file) 66 | Start-Process powershell.exe -ArgumentList "node $mainfile" 67 | 68 | if (!$?) 69 | { 70 | Pop-Location 71 | throw "Failed to run node for: '$file'." 72 | } 73 | 74 | Pop-Location 75 | Write-Message 'Node ran successfully.' 76 | } 77 | 78 | function Test-Module($colour, $variables, $credentials) 79 | { 80 | $file = Replace-Variables $colour.file $variables 81 | if ([string]::IsNullOrWhiteSpace($file)) 82 | { 83 | throw 'No file passed to run for node.' 84 | } 85 | 86 | $npm = Replace-Variables $colour.npmInstall $variables 87 | if (![string]::IsNullOrWhiteSpace($npm) -and $npm -ne $true -and $npm -ne $false) 88 | { 89 | throw "Invalid value for npmInstall: '$npm'. Should be either true or false." 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Modules/npm.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "npm", 15 | # "ensure": "install", 16 | # "global": false, 17 | # "args": "--save", 18 | # "packages": { 19 | # "express": "@4.13.4", 20 | # "mongoose": "" 21 | # }, 22 | # "path": "C:\\to\\install\\packages" 23 | # } 24 | # ] 25 | # } 26 | ######################################################################### 27 | 28 | # Uses npm to install, upgrade or uninstall the speicified packages 29 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 30 | 31 | function Start-Module($colour, $variables, $credentials) 32 | { 33 | Test-Module $colour $variables $credentials 34 | 35 | # Check to see if npm is installed, if not then install it 36 | if (!(Test-Software 'npm help' 'nodejs')) 37 | { 38 | Write-Warnings 'npm is not installed' 39 | Install-AdhocSoftware 'nodejs.install' 'node.js' 40 | } 41 | 42 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 43 | $packages = $colour.packages 44 | 45 | $path = Replace-Variables $colour.path $variables 46 | if ([string]::IsNullOrWhiteSpace($path)) 47 | { 48 | $path = [string]::Empty 49 | } 50 | elseif (!(Test-Path $path)) 51 | { 52 | throw "Path does not exist: '$path'." 53 | } 54 | 55 | $globalModules = Replace-Variables $colour.global $variables 56 | if ([string]::IsNullOrWhiteSpace($globalModules)) 57 | { 58 | $globalModules = $false 59 | } 60 | 61 | $globalTag = [string]::Empty 62 | if ($globalModules -eq $true) 63 | { 64 | $globalTag = '-g' 65 | } 66 | 67 | $_args = Replace-Variables $colour.args $variables 68 | if ($_args -eq $null) 69 | { 70 | $_args = [string]::Empty 71 | } 72 | 73 | # Set location as path pass to npm installed 74 | if (![string]::IsNullOrWhiteSpace($path)) 75 | { 76 | Push-Location $path 77 | } 78 | 79 | try 80 | { 81 | if ($packages -eq $null) 82 | { 83 | Write-Message "Starting npm $ensure." 84 | 85 | Run-Command 'npm' "$ensure $globalTag $_args" 86 | 87 | Write-Message "npm $ensure successful." 88 | Reset-Path $false 89 | } 90 | else 91 | { 92 | $keys = $packages.psobject.properties.name 93 | ForEach ($key in $keys) 94 | { 95 | # Grab the package we're dealing with currently 96 | $key = (Replace-Variables $key $variables).Trim() 97 | 98 | # What version of that package do we need? 99 | $version = Replace-Variables $packages.$key $variables 100 | if ([string]::IsNullOrWhiteSpace($version)) 101 | { 102 | $version = [string]::Empty 103 | } 104 | 105 | $version = $version.Trim() 106 | 107 | # Store the current ensure value, as this might be changed to update later 108 | $current_ensure = $ensure 109 | 110 | # Deal with the ensure value 111 | $nothing_to_do = $false 112 | switch ($current_ensure) 113 | { 114 | 'install' 115 | { 116 | if (![string]::IsNullOrWhiteSpace($version)) 117 | { 118 | npm list "$key$version" $globalTag | Out-Null 119 | if ($?) 120 | { 121 | Write-Information "$key$version is already installed." 122 | $nothing_to_do = $true 123 | } 124 | } 125 | 126 | if (!$nothing_to_do) 127 | { 128 | npm list $key $globalTag | Out-Null 129 | if ($?) 130 | { 131 | $current_ensure = 'update' 132 | } 133 | } 134 | } 135 | 136 | 'uninstall' 137 | { 138 | npm list $key $globalTag | Out-Null 139 | if (!$?) 140 | { 141 | Write-Information "$key is already uninstalled." 142 | $nothing_to_do = $true 143 | } 144 | } 145 | } 146 | 147 | # If package already (un)installed, skip to next 148 | if ($nothing_to_do) 149 | { 150 | continue 151 | } 152 | 153 | if ([string]::IsNullOrWhiteSpace($version)) 154 | { 155 | $versionStr = 'latest' 156 | } 157 | else 158 | { 159 | $versionStr = $version 160 | } 161 | 162 | Write-Message "Starting $current_ensure of $key, version: $versionStr" 163 | 164 | Run-Command 'npm' "$current_ensure $key$version $globalTag $_args" $false $true 165 | 166 | Write-Message "$current_ensure of $key ($versionStr) successful." 167 | Reset-Path $false 168 | Write-NewLine 169 | } 170 | } 171 | } 172 | finally 173 | { 174 | if (![string]::IsNullOrWhiteSpace($path)) 175 | { 176 | Pop-Location 177 | } 178 | } 179 | } 180 | 181 | function Test-Module($colour, $variables, $credentials) 182 | { 183 | # Get ensured operation for installing/uninstalling 184 | $ensure = Replace-Variables $colour.ensure $variables 185 | $ensures = @('install', 'uninstall') 186 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 187 | { 188 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 189 | } 190 | 191 | # Check to see if any packages were supplied 192 | $packages = $colour.packages 193 | if ($packages -ne $null) 194 | { 195 | # Grab the names of the packages, ensure we have valid values 196 | $keys = $packages.psobject.properties.name 197 | if ($keys -eq $null -or $keys.Length -eq 0) 198 | { 199 | throw 'No package names have been supplied.' 200 | } 201 | 202 | if (($keys | Where-Object { [string]::IsNullOrWhiteSpace((Replace-Variables $_ $variables)) } | Measure-Object).Count -gt 0) 203 | { 204 | throw 'Invalid or empty package names found.' 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/Modules/nuget.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "nuget", 15 | # "ensure": "install", 16 | # "packages": { 17 | # "NewtonSoft.Json": "latest", 18 | # "JQuery": "2.2.2" 19 | # }, 20 | # "path": "C:\\to\\install\\packages" 21 | # }, 22 | # { 23 | # "type": "nuget", 24 | # "ensure": "install", 25 | # "source": "C:\\path\\to\\soltuion.sln" 26 | # } 27 | # ] 28 | # } 29 | ######################################################################### 30 | 31 | # Uses NuGet to install, upgrade or restore the speicified packages 32 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 33 | 34 | function Start-Module($colour, $variables, $credentials) 35 | { 36 | Test-Module $colour $variables $credentials 37 | 38 | # Check to see if NuGet is installed, if not then install it 39 | if (!(Test-Software 'nuget.exe help' 'NuGet')) 40 | { 41 | Write-Warnings 'NuGet is not installed' 42 | Install-AdhocSoftware 'NuGet.CommandLine' 'NuGet' 43 | } 44 | 45 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 46 | $packages = $colour.packages 47 | 48 | $path = Replace-Variables $colour.path $variables 49 | if ([string]::IsNullOrWhiteSpace($path)) 50 | { 51 | $path = [string]::Empty 52 | } 53 | elseif (!(Test-Path $path)) 54 | { 55 | throw "Path does not exist: '$path'." 56 | } 57 | 58 | $source = Replace-Variables $colour.source $variables 59 | if ([string]::IsNullOrWhiteSpace($source)) 60 | { 61 | $source = [string]::Empty 62 | } 63 | elseif (!(Test-Path $source)) 64 | { 65 | throw "Source path does not exist: '$source'." 66 | } 67 | 68 | if ($packages -eq $null) 69 | { 70 | Write-Message "Restoring NuGet packages." 71 | 72 | Run-Command 'nuget.exe' "restore $source" 73 | 74 | Write-Message "NuGet packages restored successfully." 75 | Reset-Path $false 76 | } 77 | else 78 | { 79 | # Set location as path pass to nuget installed 80 | if (![string]::IsNullOrWhiteSpace($path)) 81 | { 82 | Push-Location $path 83 | } 84 | 85 | try 86 | { 87 | $keys = $packages.psobject.properties.name 88 | ForEach ($key in $keys) 89 | { 90 | # Grab the package we're dealing with currently 91 | $key = (Replace-Variables $key $variables).Trim() 92 | 93 | # What version of that package do we need? 94 | $version = Replace-Variables $packages.$key $variables 95 | if ([string]::IsNullOrWhiteSpace($version)) 96 | { 97 | $version = [string]::Empty 98 | } 99 | else 100 | { 101 | $version = $version.Trim() 102 | } 103 | 104 | if ([string]::IsNullOrWhiteSpace($version)) 105 | { 106 | $versionTag = [string]::Empty 107 | $version = [string]::Empty 108 | $versionStr = 'latest' 109 | } 110 | else 111 | { 112 | $versionTag = '-v' 113 | $versionStr = $version 114 | } 115 | 116 | Write-Message "Starting $ensure of $key, version: $versionStr" 117 | 118 | Run-Command 'nuget.exe' "$ensure $key $versionTag $version" 119 | 120 | Write-Message "$ensure of $key ($versionStr) successful." 121 | Reset-Path $false 122 | Write-NewLine 123 | } 124 | } 125 | finally 126 | { 127 | if (![string]::IsNullOrWhiteSpace($path)) 128 | { 129 | Pop-Location 130 | } 131 | } 132 | } 133 | } 134 | 135 | function Test-Module($colour, $variables, $credentials) 136 | { 137 | # Get ensured operation for installing/uninstalling 138 | $ensure = Replace-Variables $colour.ensure $variables 139 | $ensures = @('install', 'restore') 140 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 141 | { 142 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 143 | } 144 | 145 | switch ($ensure.ToLower()) 146 | { 147 | 'install' 148 | { 149 | # Check to see if any packages were supplied 150 | $packages = $colour.packages 151 | if ($packages -ne $null) 152 | { 153 | # Grab the names of the packages, ensure we have valid values 154 | $keys = $packages.psobject.properties.name 155 | if ($keys -eq $null -or $keys.Length -eq 0) 156 | { 157 | throw 'No package names have been supplied.' 158 | } 159 | 160 | if (($keys | Where-Object { [string]::IsNullOrWhiteSpace((Replace-Variables $_ $variables)) } | Measure-Object).Count -gt 0) 161 | { 162 | throw 'Invalid or empty package names found.' 163 | } 164 | } 165 | } 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/Modules/nunit.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "nunit", 15 | # "toolpath": "C:\\path\\to\\nunit-console.exe", 16 | # "arguments": "/include:UnitTest /nologo", 17 | # "tests": [ 18 | # "Example\\Test1.dll", 19 | # "Example\\Test2.dll" 20 | # ] 21 | # } 22 | # ] 23 | # } 24 | ######################################################################### 25 | 26 | # Use NUnit to run tests 27 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 28 | 29 | function Start-Module($colour, $variables, $credentials) 30 | { 31 | Test-Module $colour $variables $credentials 32 | 33 | $toolpath = (Replace-Variables $colour.toolpath $variables).Trim() 34 | if (!(Test-Path $toolpath)) 35 | { 36 | throw "Path to nunit-console.exe does not exist: '$toolpath'." 37 | } 38 | 39 | $tests = $colour.tests 40 | 41 | $_args = Replace-Variables $colour.arguments $variables 42 | if ($_args -eq $null) { 43 | $_args = [string]::Empty 44 | } 45 | 46 | ForEach ($test in $tests) 47 | { 48 | $test = (Replace-Variables $test $variables).Trim() 49 | 50 | if (!(Test-Path $test)) 51 | { 52 | throw "Path to test does not exist: '$test'." 53 | } 54 | } 55 | 56 | Write-Information "Arguments: '$_args'." 57 | Write-Message 'Running tests.' 58 | 59 | $test_string = Replace-Variables ($tests -join ' ') $variables 60 | Run-Command $toolpath "$test_string $_args" 61 | 62 | Write-Message 'Tests ran successfully.' 63 | } 64 | 65 | function Test-Module($colour, $variables, $credentials) 66 | { 67 | $toolpath = Replace-Variables $colour.toolpath $variables 68 | if ([String]::IsNullOrWhiteSpace($toolpath)) 69 | { 70 | throw 'No tool path specified to the location of NUint.' 71 | } 72 | 73 | $tests = $colour.tests 74 | if ($tests -eq $null -or $tests.Length -eq 0) 75 | { 76 | throw 'No tests have been supplied for NUnit.' 77 | } 78 | 79 | ForEach ($test in $tests) 80 | { 81 | $test = Replace-Variables $test $variables 82 | 83 | if ([string]::IsNullOrWhiteSpace($test)) 84 | { 85 | throw 'No path specified for tests.' 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Modules/picassio.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "picassio", 15 | # "ensure": "paint", 16 | # "palettes": [ 17 | # "C:\\path\\to\\picassio.palette" 18 | # ] 19 | # } 20 | # ] 21 | # } 22 | ######################################################################### 23 | 24 | # Paints/Erases a machine using a passed Picassio script 25 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 26 | 27 | function Start-Module($colour, $variables, $credentials) 28 | { 29 | Test-Module $colour $variables $credentials 30 | 31 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 32 | 33 | $palettes = $colour.palettes 34 | ForEach ($palette in $palettes) 35 | { 36 | $palette = Replace-Variables $palette $variables 37 | 38 | if (!(Test-Path $palette)) 39 | { 40 | throw "Palette does not exist: '$palette'." 41 | } 42 | } 43 | 44 | ForEach ($palette in $palettes) 45 | { 46 | Write-Host ([string]::Empty) 47 | $palette = Replace-Variables $palette $variables 48 | 49 | switch ($ensure) 50 | { 51 | 'paint' 52 | { 53 | Write-Message "Painting current machine." 54 | 55 | powershell.exe /C "picassio -palette `"$palette`" -paint" 56 | if (!$?) 57 | { 58 | throw "Painting palette '$palette' failed." 59 | } 60 | 61 | Write-Message 'Painting successful.' 62 | } 63 | 64 | 'erase' 65 | { 66 | Write-Message "Erasing current machine." 67 | 68 | powershell.exe /C "picassio -palette `"$palette`" -erase" 69 | if (!$?) 70 | { 71 | throw "Erasing palette '$palette' failed." 72 | } 73 | 74 | Write-Message 'Erasing successful.' 75 | } 76 | } 77 | } 78 | } 79 | 80 | function Test-Module($colour, $variables, $credentials) 81 | { 82 | # check we have a valid palettes 83 | $palettes = $colour.palettes 84 | if ($palettes -eq $null -or $palettes.Length -eq 0) 85 | { 86 | throw 'No palettes have been supplied.' 87 | } 88 | 89 | ForEach ($palette in $palettes) 90 | { 91 | $palette = Replace-Variables $palette $variables 92 | 93 | if ([string]::IsNullOrEmpty($palette)) 94 | { 95 | throw 'No palette path has been supplied.' 96 | } 97 | } 98 | 99 | # check we have a valid ensure property 100 | $ensure = (Replace-Variables $colour.ensure $variables) 101 | $ensures = @('paint', 'erase') 102 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 103 | { 104 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Modules/ps-sql.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example (still in dev, only works for sql and backups): 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "ps-sql", 15 | # "timeout": 60, 16 | # "server": "192.130.1.90\INSTANCE", 17 | # "backup": { 18 | # "type": "restore", 19 | # "location": "C:\\path\\to\\backup.bac", 20 | # "database": "DatabaseName", 21 | # "sqlpath": "C:\\path\\to\\put\\mdf_and_ldf" 22 | # }, 23 | # "sql": { 24 | # "query": "SELECT * FROM [Example]", 25 | # "file": "C:\\path\\to\\script.sql" 26 | # } 27 | # } 28 | # ] 29 | # } 30 | ######################################################################### 31 | 32 | # Run specific SQL commands, or general functions using SQL Servers PowerShell module 33 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 34 | 35 | function Start-Module($colour, $variables, $credentials) 36 | { 37 | Test-Module $colour $variables $credentials 38 | Start-SqlPs $colour.sqlpsPath 39 | 40 | $server = Replace-Variables $colour.server $variables 41 | $timeout = Replace-Variables $colour.timeout $variables 42 | 43 | if ([string]::IsNullOrWhiteSpace($timeout)) 44 | { 45 | Write-Message 'No timeout value specified, using default of 60 seconds.' 46 | $timeout = 60 47 | } 48 | 49 | Write-Information "Using database server $server" 50 | 51 | # Snapshot 52 | 53 | # Backup 54 | $backup = $colour.backup 55 | if ($backup -ne $null) 56 | { 57 | $backupType = (Replace-Variables $backup.type $variables).ToLower().Trim() 58 | $backupLocation = Replace-Variables $backup.location $variables 59 | $backupDatabase = Replace-Variables $backup.database $variables 60 | 61 | switch ($backupType) 62 | { 63 | 'restore' 64 | { 65 | $backupSqlPath = Replace-Variables $backup.sqlpath $variables 66 | if (!(Test-Path $backupSqlPath)) 67 | { 68 | throw "Backup SQL path does not exist: '$backupSqlPath'." 69 | } 70 | 71 | Restore-Backup $backupLocation $backupDatabase $backupSqlPath $server $timeout 72 | } 73 | 74 | 'create' 75 | { 76 | Create-Backup $backupLocation $backupDatabase $server $timeout 77 | } 78 | } 79 | } 80 | 81 | # SQL 82 | $sql = $colour.sql 83 | if ($sql -ne $null) 84 | { 85 | $query = Replace-Variables $sql.query $variables 86 | $file = Replace-Variables $sql.file $variables 87 | 88 | Run-Sql $query $file $server $timeout 89 | } 90 | } 91 | 92 | function Test-Module($colour, $variables, $credentials) 93 | { 94 | Test-SqlPs 95 | 96 | # General server/timeout 97 | $backup = $colour.backup 98 | $snapshot = $colour.snapshot 99 | $sql = $colour.sql 100 | 101 | if (($backup -ne $null -and ($snapshot -ne $null -or $sql -ne $null)) -or ($snapshot -ne $null -and $sql -ne $null)) 102 | { 103 | throw 'You can on specify one of either backup, snapshot or sql for mssql.' 104 | } 105 | 106 | $server = Replace-Variables $colour.server $variables 107 | if ([string]::IsNullOrWhiteSpace($server)) 108 | { 109 | throw 'No database server specified.' 110 | } 111 | 112 | $timeout = Replace-Variables $colour.timeout $variables 113 | if (![string]::IsNullOrWhiteSpace($timeout) -and ($timeout -notmatch "^[0-9]+$")) 114 | { 115 | throw "Invalid value for timeout: '$timeout'. Should be an integer value." 116 | } 117 | 118 | # Snapshot 119 | if ($snapshot -ne $null) 120 | { 121 | 122 | } 123 | 124 | # Backup 125 | if ($backup -ne $null) 126 | { 127 | $backupType = Replace-Variables $backup.type $variables 128 | if ([string]::IsNullOrWhiteSpace($backupType)) 129 | { 130 | throw 'No backup type specified.' 131 | } 132 | 133 | $backupType = $backupType.ToLower().Trim() 134 | if ($backupType -ne 'create' -and $backupType -ne 'restore') 135 | { 136 | throw "Invalid backup type parameter supplied: '$backupType'." 137 | } 138 | 139 | $backupLocation = Replace-Variables $backup.location $variables 140 | if ([string]::IsNullOrWhiteSpace($backupLocation)) 141 | { 142 | throw 'No backup location specified.' 143 | } 144 | 145 | $backupDatabase = Replace-Variables $backup.database $variables 146 | if ([string]::IsNullOrWhiteSpace($backupDatabase)) 147 | { 148 | throw 'No backup database name specified.' 149 | } 150 | 151 | if ($backupType -eq 'restore') 152 | { 153 | $backupSqlPath = Replace-Variables $backup.sqlpath $variables 154 | if ([string]::IsNullOrWhiteSpace($backupSqlPath)) 155 | { 156 | throw 'No backup SQL path specified.' 157 | } 158 | } 159 | } 160 | 161 | # SQL 162 | if ($sql -ne $null) 163 | { 164 | $query = Replace-Variables $sql.query $variables 165 | $file = Replace-Variables $sql.file $variables 166 | 167 | if ([string]::IsNullOrWhiteSpace($query) -and [string]::IsNullOrWhiteSpace($file)) 168 | { 169 | throw 'Either a SQL query or file path need to be supplied.' 170 | } 171 | elseif (![string]::IsNullOrWhiteSpace($query) -and ![string]::IsNullOrWhiteSpace($file)) 172 | { 173 | throw 'Only one of either a SQL query or file path can be supplied.' 174 | } 175 | } 176 | } 177 | 178 | 179 | ################### 180 | # SQL POWERSHELL 181 | ################### 182 | function Test-SqlPs() 183 | { 184 | $sqlpsreg = 'HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.SqlServer.Management.PowerShell.sqlps' 185 | if (Get-ChildItem $sqlpsreg -ErrorAction SilentlyContinue) 186 | { 187 | throw 'SQL Server Powershell is not installed.' 188 | } 189 | 190 | return $true 191 | } 192 | 193 | function Start-SqlPs($sqlpsPath) 194 | { 195 | # Based off work by Michiel Wories 196 | if ((Test-SqlPs) -and [String]::IsNullOrWhiteSpace($sqlpsPath)) 197 | { 198 | $sqlpsPath = "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\" 199 | } 200 | 201 | if (!(Test-Path $sqlpsPath)) 202 | { 203 | throw "Path to sqlps does not exist: '$sqlpsPath'." 204 | } 205 | 206 | # Preload the assemblies. Note that most assemblies will be loaded when the provider 207 | # is used. if you work only within the provider this may not be needed. It will reduce 208 | # the shell's footprint if you leave these out. 209 | $assemblylist = 210 | "Microsoft.SqlServer.Smo", 211 | "Microsoft.SqlServer.Dmf ", 212 | "Microsoft.SqlServer.SqlWmiManagement ", 213 | "Microsoft.SqlServer.ConnectionInfo ", 214 | "Microsoft.SqlServer.SmoExtended ", 215 | "Microsoft.SqlServer.Management.RegisteredServers ", 216 | "Microsoft.SqlServer.Management.Sdk.Sfc ", 217 | "Microsoft.SqlServer.SqlEnum ", 218 | "Microsoft.SqlServer.RegSvrEnum ", 219 | "Microsoft.SqlServer.WmiEnum ", 220 | "Microsoft.SqlServer.ServiceBrokerEnum ", 221 | "Microsoft.SqlServer.ConnectionInfoExtended ", 222 | "Microsoft.SqlServer.Management.Collector ", 223 | "Microsoft.SqlServer.Management.CollectorEnum" 224 | 225 | foreach ($asm in $assemblylist) 226 | { 227 | $asm = [Reflection.Assembly]::LoadWithPartialName($asm) 228 | } 229 | 230 | # Set variables that the provider expects (mandatory for the SQL provider) 231 | Set-Variable -scope Global -name SqlServerMaximumChildItems -Value 0 232 | Set-Variable -scope Global -name SqlServerConnectionTimeout -Value 30 233 | Set-Variable -scope Global -name SqlServerIncludeSystemObjects -Value $false 234 | Set-Variable -scope Global -name SqlServerMaximumTabCompletion -Value 1000 235 | 236 | try 237 | { 238 | # Load the snapins, type data, format data 239 | Push-Location $sqlpsPath 240 | 241 | Add-PSSnapin SqlServerCmdletSnapin100 242 | Add-PSSnapin SqlServerProviderSnapin100 243 | Update-TypeData -PrependPath SQLProvider.Types.ps1xml 244 | update-FormatData -prependpath SQLProvider.Format.ps1xml 245 | 246 | Write-Information 'SQL Server Powershell extensions are loaded.' 247 | } 248 | finally 249 | { 250 | Pop-Location 251 | } 252 | } 253 | 254 | 255 | ################### 256 | # SNAPSHOTS 257 | ################### 258 | function Create-Shapshot($snapshotName, $snapshotLocation, $databaseName, $server, $timeoutSeconds) 259 | { 260 | Write-Message "Creating snapshot of [$databaseName]" 261 | 262 | $query = " 263 | USE [$databaseName]; 264 | SELECT FILE_NAME(1);" 265 | 266 | $result = Run-Sql -query $query -server $server 267 | if (!$?) 268 | { 269 | throw 'Failed to retrieve database name for snapshot.' 270 | } 271 | 272 | $filename = $result[0] 273 | $query = " 274 | CREATE DATABASE $snapshotName ON ( 275 | Name = $filename, 276 | FILENAME = '$snapshotLocation' 277 | ) AS SNAPSHOT OF [$databaseName];" 278 | 279 | $result = Run-Sql -query $query -server $server -timeoutSeconds $timeoutSeconds 280 | if (!$?) 281 | { 282 | throw 'Failed to create snapshot for database.' 283 | } 284 | 285 | Write-Host 'Snapshot created.' 286 | } 287 | 288 | function Restore-Shapshot($snapshotName, $databaseName, $server, $timeoutSeconds) 289 | { 290 | Write-Message "Restoring database for [$databaseName]" 291 | 292 | $query = " 293 | ALTER DATABASE [$databaseName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; 294 | RESTORE DATABASE [$databaseName] FROM DATABASE_SNAPSHOT = '$snapshotName'; 295 | ALTER DATABASE [$databaseName] SET MULTI_USER;" 296 | 297 | $result = Run-Sql -query $query -server $server -timeoutSeconds $timeoutSeconds 298 | if (!$?) 299 | { 300 | throw 'Failed to restore database from snapshot.' 301 | } 302 | 303 | Write-Host 'Database restored.' 304 | } 305 | 306 | 307 | ################### 308 | # BACKUPS 309 | ################### 310 | function Create-Backup($backupLocation, $databaseName, $server, $timeoutSeconds) 311 | { 312 | Write-Message "Creating backup of [$databaseName]" 313 | 314 | $query = " 315 | BACKUP DATABASE [$databaseName] 316 | TO DISK = '$backupLocation' WITH INIT;" 317 | 318 | $result = Run-Sql -query $query -server $server -timeout $timeoutSeconds 319 | if (!$?) 320 | { 321 | throw 'Failed to create backup of the database.' 322 | } 323 | 324 | Write-Host 'Backup created.' 325 | } 326 | 327 | function Restore-Backup($backupLocation, $databaseName, $sqlPath, $server, $timeoutSeconds) 328 | { 329 | Write-Message "Restoring backup of [$databaseName]" 330 | 331 | $query = " 332 | DECLARE @FileList TABLE 333 | ( 334 | LogicalName NVARCHAR(128) NOT NULL, 335 | PhysicalName NVARCHAR(260) NOT NULL, 336 | Type CHAR(1) NOT NULL, 337 | FileGroupName NVARCHAR(120) NULL, 338 | Size NUMERIC(20, 0) NOT NULL, 339 | MaxSize NUMERIC(20, 0) NOT NULL, 340 | FileID BIGINT NULL, 341 | CreateLSN NUMERIC(25,0) NULL, 342 | DropLSN NUMERIC(25,0) NULL, 343 | UniqueID UNIQUEIDENTIFIER NULL, 344 | ReadOnlyLSN NUMERIC(25,0) NULL , 345 | ReadWriteLSN NUMERIC(25,0) NULL, 346 | BackupSizeInBytes BIGINT NULL, 347 | SourceBlockSize INT NULL, 348 | FileGroupID INT NULL, 349 | LogGroupGUID UNIQUEIDENTIFIER NULL, 350 | DifferentialBaseLSN NUMERIC(25,0)NULL, 351 | DifferentialBaseGUID UNIQUEIDENTIFIER NULL, 352 | IsReadOnly BIT NULL, 353 | IsPresent BIT NULL, 354 | TDEThumbprint VARBINARY(32) NULL 355 | ); 356 | 357 | INSERT INTO @FileList 358 | EXEC(N'RESTORE FILELISTONLY FROM DISK = ''$backupLocation'';'); 359 | 360 | DECLARE @logical_data NVARCHAR(128), @logical_log NVARCHAR(128); 361 | 362 | SET @logical_data = (SELECT LogicalName FROM @FileList WHERE Type = 'D' AND FileID = 1); 363 | SET @logical_log = (SELECT LogicalName FROM @FileList WHERE Type = 'L' AND FileID = 2); 364 | 365 | RESTORE DATABASE [$databaseName] 366 | FROM DISK = N'$backupLocation' 367 | WITH REPLACE, 368 | MOVE @logical_data TO '$sqlPath\$databaseName.mdf', 369 | MOVE @logical_log TO '$sqlPath\$databaseName.ldf';" 370 | 371 | $result = Run-Sql -query $query -server $server -timeout $timeoutSeconds 372 | if (!$?) 373 | { 374 | throw 'Failed to restore database from backup.' 375 | } 376 | 377 | Write-Host 'Database restored.' 378 | } 379 | 380 | 381 | ################### 382 | # SQL 383 | ################### 384 | function Run-Sql($query, $file, $server, $timeoutSeconds = 60) 385 | { 386 | if (![string]::IsNullOrWhiteSpace($query)) 387 | { 388 | Write-Host 'Running SQL query.' 389 | Write-Information "$query" 390 | $value = Invoke-Sqlcmd -Query $query -ServerInstance $server -QueryTimeout $timeoutSeconds 391 | } 392 | else 393 | { 394 | Write-Host 'Running SQL file' 395 | 396 | if (!(Test-Path $file)) 397 | { 398 | throw "SQL file path does not exist: '$file'." 399 | } 400 | 401 | Write-Information "File: '$file'" 402 | $value = Invoke-Sqlcmd -InputFile $file -ServerInstance $server -QueryTimeout $timeoutSeconds 403 | } 404 | 405 | if (!$?) 406 | { 407 | throw 'Failed to run SQL command.' 408 | } 409 | 410 | return $value 411 | } 412 | -------------------------------------------------------------------------------- /src/Modules/service.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "service", 15 | # "name": "Example Service", 16 | # "path": "C:\\absolute\\path\\to\\service.exe", 17 | # "ensure": "installed", 18 | # "state": "started" 19 | # } 20 | # ] 21 | # } 22 | ######################################################################### 23 | 24 | # Installs a service onto the system 25 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 26 | 27 | function Start-Module($colour, $variables, $credentials) 28 | { 29 | Test-Module $colour $variables $credentials 30 | 31 | # attempt to retrieve the service 32 | $name = (Replace-Variables $colour.name $variables).Trim() 33 | $service = (Get-WmiObject -Class Win32_Service -Filter "Name='$name'") 34 | 35 | # check if service is already uninstalled 36 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 37 | if ($service -eq $null -and $ensure -eq 'uninstalled') 38 | { 39 | Write-Message "Service '$name' already $ensure." 40 | return 41 | } 42 | 43 | $state = Replace-Variables $colour.state $variables 44 | if ($state -ne $null) 45 | { 46 | $state = $state.ToLower().Trim() 47 | } 48 | 49 | $path = Replace-Variables $colour.path $variables 50 | if ($path -ne $null) 51 | { 52 | $path = $path.Trim() 53 | } 54 | 55 | # Deal with exists logic 56 | if ($ensure -eq 'exists') 57 | { 58 | if ($service -eq $null) 59 | { 60 | Write-Message 'Service does not exist, skipping exists state logic.' 61 | return 62 | } 63 | 64 | Write-Message "Ensuring service '$name' is $state." 65 | Toggle-Service $name $state 66 | Write-Message "Service $state." 67 | return 68 | } 69 | 70 | if ($service -ne $null -and $ensure -eq 'installed') 71 | { 72 | Write-Message "Ensuring service '$name' is $state." 73 | Toggle-Service $name $state 74 | Write-Message "Service $state." 75 | } 76 | elseif ($service -ne $null -and $ensure -eq 'uninstalled') 77 | { 78 | Write-Message "Ensuring service '$name' is $ensure." 79 | 80 | $tasks = (tasklist /FI "IMAGENAME eq mmc.exe") 81 | $t = ($tasks | Where-Object { $_ -match "mmc.exe" }) 82 | if ($t.Count -gt 0) 83 | { 84 | taskkill /F /IM mmc.exe | Out-Null 85 | } 86 | 87 | Stop-Service $name | Out-Null 88 | if (!$?) 89 | { 90 | throw 'Failed to stop service before deletion.' 91 | } 92 | 93 | $service.delete() | Out-Null 94 | if (!$?) 95 | { 96 | throw 'Failed to delete service.' 97 | } 98 | 99 | Write-Message "Service $ensure." 100 | } 101 | else 102 | { 103 | if (!(Test-Path $path)) 104 | { 105 | throw "Path passed to install service does not exist: '$path'" 106 | } 107 | 108 | Write-Message "Ensuring service '$name' is $ensure." 109 | 110 | New-Service -Name $name -BinaryPathName $path -StartupType Automatic 111 | if (!$?) 112 | { 113 | throw 'Failed to create the service.' 114 | } 115 | 116 | Write-Message "Service $ensure." 117 | 118 | Write-Message "Ensuring service '$name' is $state." 119 | Toggle-Service $name $state $false 120 | Write-Message "Service $state." 121 | } 122 | } 123 | 124 | function Test-Module($colour, $variables, $credentials) 125 | { 126 | $name = Replace-Variables $colour.name $variables 127 | if ([string]::IsNullOrWhiteSpace($name)) 128 | { 129 | throw 'No service name supplied.' 130 | } 131 | 132 | $name = $name.Trim() 133 | 134 | # attempt to retrieve the service 135 | $service = (Get-WmiObject -Class Win32_Service -Filter "Name='$name'") 136 | 137 | $ensure = Replace-Variables $colour.ensure $variables 138 | if ([string]::IsNullOrWhiteSpace($ensure)) 139 | { 140 | throw 'No ensure parameter supplied for service.' 141 | } 142 | 143 | # check we have a valid ensure property 144 | $ensure = $ensure.ToLower().Trim() 145 | if ($ensure -ne 'installed' -and $ensure -ne 'uninstalled' -and $ensure -ne 'exists') 146 | { 147 | throw "Invalid ensure parameter supplied for service: '$ensure'." 148 | } 149 | 150 | $state = Replace-Variables $colour.state $variables 151 | if ([string]::IsNullOrWhiteSpace($state) -and ($ensure -eq 'installed' -or $ensure -eq 'exists')) 152 | { 153 | throw 'No state parameter supplied for service.' 154 | } 155 | 156 | # check we have a valid state property 157 | if ($state -ne $null) 158 | { 159 | $state = $state.ToLower().Trim() 160 | } 161 | 162 | if ($state -ne 'started' -and $state -ne 'stopped' -and ($ensure -eq 'installed' -or $ensure -eq 'exists')) 163 | { 164 | throw "Invalid state parameter supplied for service: '$state'." 165 | } 166 | 167 | $path = Replace-Variables $colour.path $variables 168 | if ([string]::IsNullOrWhiteSpace($path) -and $service -eq $null -and $ensure -eq 'installed') 169 | { 170 | throw 'No path passed to install service.' 171 | } 172 | } 173 | 174 | 175 | function Toggle-Service($name, $state, $restart = $true) 176 | { 177 | if ($state -eq 'started') 178 | { 179 | if ($restart) 180 | { 181 | Restart-Service $name -Force 182 | } 183 | else 184 | { 185 | Start-Service $name 186 | } 187 | } 188 | else 189 | { 190 | Stop-Service $name -Force 191 | } 192 | 193 | if (!$?) 194 | { 195 | throw "Failed to update service state." 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Modules/ssdt.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "ssdt", 15 | # "action": "publish", 16 | # "path": "C:\\path\\to\\SqlPackage.exe", 17 | # "source": "C:\\path\\to\\some\\file.dacpac", 18 | # "publish": "C:\\path\\to\\some\\publish.xml", 19 | # "timeout": 60, 20 | # "backupFirst": false, 21 | # "dropFirst": false, 22 | # "blockOnLoss": true, 23 | # "arguments": "/p:IgnorePermissions=True" 24 | # }, 25 | # { 26 | # "type": "ssdt", 27 | # "action": "script", 28 | # "path": "C:\\path\\to\\SqlPackage.exe", 29 | # "source": "C:\\path\\to\\some\\file.dacpac", 30 | # "output": "C:\\path\\to\\create\\output.sql", 31 | # "timeout": 60, 32 | # "arguments": "/p:IgnorePermissions=True" 33 | # } 34 | # ] 35 | # } 36 | ######################################################################### 37 | 38 | # Publishes or generates the script via SSDT's SqlPackage tool 39 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 40 | 41 | function Start-Module($colour, $variables, $credentials) 42 | { 43 | Test-Module $colour $variables $credentials 44 | 45 | $path = (Replace-Variables $colour.path $variables) 46 | if ([string]::IsNullOrWhiteSpace($path)) 47 | { 48 | Write-Message 'No path supplied, using default.' 49 | $path = 'C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe' 50 | } 51 | else 52 | { 53 | $path = $path.Trim() 54 | } 55 | 56 | $action = (Replace-Variables $colour.action $variables).Trim().ToLower() 57 | 58 | $source = (Replace-Variables $colour.source $variables).Trim() 59 | $source = Resolve-Path $source 60 | 61 | $timeout = Replace-Variables $colour.timeout $variables 62 | if ([string]::IsNullOrWhiteSpace($timeout)) 63 | { 64 | $timeoutAction = [string]::Empty 65 | } 66 | else 67 | { 68 | $timeoutAction = "/p:CommandTimeout=$timeout" 69 | } 70 | 71 | $_args = Replace-Variables $colour.arguments $variables 72 | if ([string]::IsNullOrWhiteSpace($_args)) 73 | { 74 | $_args = [string]::Empty 75 | } 76 | 77 | $final_args = "/sf:`"$source`" /a:$action $timeoutAction" 78 | 79 | switch ($action) 80 | { 81 | 'publish' 82 | { 83 | $publish = Replace-Variables $colour.publish $variables 84 | if (![string]::IsNullOrWhiteSpace($publish)) 85 | { 86 | $publish = Resolve-Path $publish 87 | $publish = "/pr:`"$publish`"" 88 | } 89 | else 90 | { 91 | $publish = [string]::Empty 92 | } 93 | 94 | $backupFirst = Replace-Variables $colour.backupFirst $variables 95 | if ([string]::IsNullOrWhiteSpace($backupFirst)) 96 | { 97 | $backupFirst = $false 98 | } 99 | 100 | $dropFirst = Replace-Variables $colour.dropFirst $variables 101 | if ([string]::IsNullOrWhiteSpace($dropFirst)) 102 | { 103 | $dropFirst = $false 104 | } 105 | 106 | $blockOnLoss = Replace-Variables $colour.blockOnLoss $variables 107 | if ([string]::IsNullOrWhiteSpace($blockOnLoss)) 108 | { 109 | $blockOnLoss = $true 110 | } 111 | 112 | $final_args = "$final_args $publish /p:BackupDatabaseBeforeChanges=$backupFirst /p:CreateNewDatabase=$dropFirst /p:BlockOnPossibleDataLoss=$blockOnLoss" 113 | } 114 | 115 | 'script' 116 | { 117 | $output = (Replace-Variables $colour.output $variables).Trim() 118 | $output = Resolve-Path $output 119 | 120 | $final_args = "$final_args /op:`"$output`"" 121 | } 122 | } 123 | 124 | if (![string]::IsNullOrWhiteSpace($_args)) 125 | { 126 | $final_args = "$final_args $_args" 127 | } 128 | 129 | Write-Message "Attempting to $action SSDT." 130 | 131 | $parentPath = Split-Path -Parent $path 132 | $toolExe = (".\{0}" -f (Split-Path -Leaf $path)) 133 | 134 | Push-Location $parentPath 135 | 136 | try 137 | { 138 | Run-Command $toolExe $final_args -isPowershell $true 139 | } 140 | finally 141 | { 142 | Pop-Location 143 | } 144 | 145 | Write-Message "$action of SSDT successful." 146 | } 147 | 148 | function Test-Module($colour, $variables, $credentials) 149 | { 150 | $action = Replace-Variables $colour.action $variables 151 | $actions = @('publish', 'script') 152 | if ([string]::IsNullOrWhiteSpace($action) -or $actions -inotcontains ($action.Trim())) 153 | { 154 | throw ("Invalid action found: '$action'. Can be only: {0}." -f ($actions -join ', ')) 155 | } 156 | 157 | $path = Replace-Variables $colour.path $variables 158 | if ([string]::IsNullOrWhiteSpace($path)) 159 | { 160 | $path = 'C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\SqlPackage.exe' 161 | } 162 | 163 | if (!(Test-Path ($path.Trim())) -and $variables['__initial_validation__'] -eq $false) 164 | { 165 | throw "Invalid path to SqlPackage.exe supplied: '$path'." 166 | } 167 | 168 | $source = Replace-Variables $colour.source $variables 169 | if ([string]::IsNullOrWhiteSpace($source) -or (!(Test-Path ($source.Trim())) -and $variables['__initial_validation__'] -eq $false)) 170 | { 171 | throw "Invalid or empty source path supplied: '$source'." 172 | } 173 | 174 | switch ($action.Trim().ToLower()) 175 | { 176 | 'publish' 177 | { 178 | $publish = Replace-Variables $colour.publish $variables 179 | if ([string]::IsNullOrWhiteSpace($publish)) 180 | { 181 | $_args = Replace-Variables $colour.arguments $variables 182 | if ([string]::IsNullOrWhiteSpace($_args)) 183 | { 184 | throw 'If no publish profile is supplied, then you must supply the appropriate arguments to pass to the SqlPackage tool.' 185 | } 186 | } 187 | elseif (!(Test-Path ($publish.Trim())) -and $variables['__initial_validation__'] -eq $false) 188 | { 189 | throw "Invalid publish profile path supplied: '$publish'." 190 | } 191 | } 192 | 193 | 'script' 194 | { 195 | $output = Replace-Variables $colour.output $variables 196 | if ([string]::IsNullOrWhiteSpace($output)) 197 | { 198 | throw "Empty output path supplied: '$output'." 199 | } 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/Modules/svn.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "svn", 15 | # "url": "https://url.to.some.svn", 16 | # "path": "C:\\path\\to\\local\\svn", 17 | # "name": "LocalName", 18 | # "revision": "12345" 19 | # } 20 | # ] 21 | # } 22 | ######################################################################### 23 | 24 | # Checkout a remote repository using svn into the supplied local path 25 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 26 | 27 | function Start-Module($colour, $variables, $credentials) 28 | { 29 | Test-Module $colour $variables $credentials 30 | 31 | if (!(Test-Software svn.exe 'svn')) 32 | { 33 | Write-Warnings 'SVN is not installed' 34 | Install-AdhocSoftware 'svn' 'SVN' 35 | } 36 | 37 | $url = (Replace-Variables $colour.url $variables).Trim() 38 | $name = (Replace-Variables $colour.name $variables).Trim() 39 | 40 | $path = (Replace-Variables $colour.path $variables).Trim() 41 | if (!(Test-Path $path)) 42 | { 43 | New-Item -ItemType Directory -Force -Path $path | Out-Null 44 | } 45 | 46 | $revision = Replace-Variables $colour.revision $variables 47 | if ($revision -ne $null) 48 | { 49 | $revision = $revision.Trim() 50 | } 51 | 52 | Push-Location $path 53 | 54 | try 55 | { 56 | # Delete existing directory 57 | if ((Test-Path $name)) 58 | { 59 | Backup-Directory $name 60 | } 61 | 62 | # checkout 63 | Write-Message "Checking out SVN repository from '$url' to '$path'." 64 | Run-Command 'svn.exe' "checkout $url $name" 65 | 66 | # reset to revision 67 | if (![string]::IsNullOrWhiteSpace($revision)) 68 | { 69 | Write-Message "Resetting local repository to revision $revision." 70 | Push-Location $name 71 | 72 | try 73 | { 74 | Run-Command 'svn.exe' "up -r $revision" 75 | } 76 | finally 77 | { 78 | Pop-Location 79 | } 80 | } 81 | 82 | Write-Message 'SVN checkout was successful.' 83 | } 84 | finally 85 | { 86 | Pop-Location 87 | } 88 | } 89 | 90 | function Test-Module($colour, $variables, $credentials) 91 | { 92 | $url = Replace-Variables $colour.url $variables 93 | if ([string]::IsNullOrWhiteSpace($url)) 94 | { 95 | throw 'No URL to an SVN repository passed.' 96 | } 97 | 98 | $path = Replace-Variables $colour.path $variables 99 | if ([string]::IsNullOrWhiteSpace($path)) 100 | { 101 | throw 'No local SVN repository path specified.' 102 | } 103 | 104 | $name = Replace-Variables $colour.name $variables 105 | if ([string]::IsNullOrWhiteSpace($name)) 106 | { 107 | throw 'No local name supplied for local repository.' 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Modules/vagrant.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "vagrant", 15 | # "path": "C:\\path\\to\\project", 16 | # "command": "up" 17 | # } 18 | # ] 19 | # } 20 | ######################################################################### 21 | 22 | # Calls vagrant from a specified path where a Vagrantfile can be found 23 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 24 | 25 | function Start-Module($colour, $variables, $credentials) 26 | { 27 | Test-Module $colour $variables $credentials 28 | 29 | if (!(Test-Software 'vagrant version' 'vagrant')) 30 | { 31 | Write-Warnings 'Vagrant is not installed' 32 | Install-AdhocSoftware 'vagrant' 'Vagrant' 33 | } 34 | 35 | $path = (Replace-Variables $colour.path $variables).Trim() 36 | $command = (Replace-Variables $colour.command $variables).Trim() 37 | 38 | if (!(Test-Path $path)) 39 | { 40 | throw "Path specified to Vagrantfile doesn't exist: '$path'." 41 | } 42 | 43 | Write-Message "Running vagrant $command." 44 | Push-Location $path 45 | 46 | try 47 | { 48 | vagrant.exe $command 49 | if (!$?) 50 | { 51 | throw 'Failed to call vagrant.' 52 | } 53 | } 54 | finally 55 | { 56 | Pop-Location 57 | } 58 | 59 | Write-Message "vagrant $command, successful." 60 | } 61 | 62 | function Test-Module($colour, $variables, $credentials) 63 | { 64 | $path = Replace-Variables $colour.path $variables 65 | if ([string]::IsNullOrWhiteSpace($path)) 66 | { 67 | throw 'No path specified to parent directory where the Vagrantfile is located.' 68 | } 69 | 70 | $command = Replace-Variables $colour.command $variables 71 | if ([string]::IsNullOrWhiteSpace($command)) 72 | { 73 | throw 'No command specified for which to call vagrant.' 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Modules/variables.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "variables", 15 | # "variables": { 16 | # "value1": true, 17 | # "value2": "added" 18 | # } 19 | # } 20 | # ] 21 | # } 22 | # 23 | # Variables are used in other values by doing: { "ensure": "#(value2)" } 24 | # which using the example above will be replaced by "added" 25 | ######################################################################### 26 | 27 | # Parses the passed variables colour and inserts/updates them 28 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 29 | 30 | function Start-Module($colour, $variables, $credentials) 31 | { 32 | Test-Module $colour $variables $credentials 33 | } 34 | 35 | function Test-Module($colour, $variables, $credentials) 36 | { 37 | $vars = $colour.variables 38 | if ($vars -eq $null) 39 | { 40 | return 41 | } 42 | 43 | $keys = $vars.psobject.properties.name 44 | if ($keys -eq $null -or $keys.Length -eq 0) 45 | { 46 | Write-Message 'No variables supplied.' 47 | return 48 | } 49 | 50 | $pattern = Get-VariableRegex 51 | 52 | $invalid = ($keys | Where-Object { $_ -inotmatch $pattern }) 53 | if ($invalid -ne $null -and $invalid.Length -gt 0) 54 | { 55 | Write-Errors "Invalid variable names found. Variable names can only be alphanumeric`n$invalid" 56 | throw 57 | } 58 | 59 | $keys | ForEach-Object { $variables[$_] = $vars.$_ } 60 | if (!$?) 61 | { 62 | throw 'Variables failed to setup.' 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Modules/windows-feature.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "windows-feature", 15 | # "ensure": "install", 16 | # "includeSubFeatures": true, 17 | # "includeManagementTools": true, 18 | # "names": [ 19 | # "Web-Server" 20 | # ] 21 | # } 22 | # ] 23 | # } 24 | ######################################################################### 25 | 26 | # Installs/uninstalled windows features 27 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 28 | Import-Module ServerManager -ErrorAction Stop 29 | 30 | function Start-Module($colour, $variables, $credentials) 31 | { 32 | Test-Module $colour $variables $credentials 33 | 34 | $names = $colour.names 35 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 36 | $includeSubFeatures = Replace-Variables $colour.includeSubFeatures $variables 37 | $includeManagementTools = Replace-Variables $colour.includeManagementTools $variables 38 | 39 | ForEach ($name in $names) 40 | { 41 | $name = (Replace-Variables $name $variables).Trim() 42 | Write-Message "`nAttempting to $ensure '$name'." 43 | 44 | switch ($ensure) 45 | { 46 | 'install' 47 | { 48 | if ((Get-WindowsFeature -Name $name).Installed -eq $true) 49 | { 50 | Write-Information ("$name has already been {0}ed." -f $ensure) 51 | continue 52 | } 53 | 54 | if ($includeSubFeatures -eq $true -and $includeManagementTools -eq $true) 55 | { 56 | Add-WindowsFeature -Name $name -IncludeAllSubFeature -IncludeManagementTools 57 | } 58 | elseif ($includeSubFeatures -eq $true) 59 | { 60 | Add-WindowsFeature -Name $name -IncludeAllSubFeature 61 | } 62 | elseif ($includeManagementTools -eq $true) 63 | { 64 | Add-WindowsFeature -Name $name -IncludeManagementTools 65 | } 66 | else 67 | { 68 | Add-WindowsFeature -Name $name 69 | } 70 | 71 | if (!$?) 72 | { 73 | throw 'Failed to install Windows feature.' 74 | } 75 | } 76 | 77 | 'uninstall' 78 | { 79 | if ((Get-WindowsFeature -Name $name).Installed -eq $false) 80 | { 81 | Write-Information ("$name has already been {0}ed." -f $ensure) 82 | continue 83 | } 84 | 85 | if ($includeManagementTools -eq $true) 86 | { 87 | Remove-WindowsFeature -Name $name -IncludeManagementTools 88 | } 89 | else 90 | { 91 | Remove-WindowsFeature -Name $name 92 | } 93 | 94 | if (!$?) 95 | { 96 | throw 'Failed to uninstall Windows feature.' 97 | } 98 | } 99 | } 100 | 101 | Write-Message "$ensure of '$name' successful." 102 | } 103 | 104 | Write-Information 'It is suggested that you restart your computer.' 105 | } 106 | 107 | function Test-Module($colour, $variables, $credentials) 108 | { 109 | # Check feature names are all valid 110 | $names = $colour.names 111 | if ($names -eq $null -or $names.Length -eq 0) 112 | { 113 | throw 'No Windows feature names have been supplied.' 114 | } 115 | 116 | ForEach ($name in $names) 117 | { 118 | $name = Replace-Variables $name $variables 119 | 120 | if ([string]::IsNullOrEmpty($name) -or (Get-WindowsFeature -Name $name | Measure-Object).Count -eq 0) 121 | { 122 | throw "Invalid Windows feature: '$name'." 123 | } 124 | } 125 | 126 | # Check ensures value 127 | $ensure = Replace-Variables $colour.ensure $variables 128 | $ensures = @('install', 'uninstall') 129 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 130 | { 131 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 132 | } 133 | 134 | # Check sub features and tools values 135 | $includeSubFeatures = Replace-Variables $colour.includeSubFeatures $variables 136 | if (![string]::IsNullOrWhiteSpace($includeSubFeatures) -and $includeSubFeatures -ne $true -and $includeSubFeatures -ne $false) 137 | { 138 | throw "Invalid value for includeSubFeatures: '$includeSubFeatures'. Should be either true or false." 139 | } 140 | 141 | $includeManagemementTools = Replace-Variables $colour.includeManagementTools $variables 142 | if (![string]::IsNullOrWhiteSpace($includeManagemementTools) -and $includeManagemementTools -ne $true -and $includeManagemementTools -ne $false) 143 | { 144 | throw "Invalid value for includeManagementTools: '$includeManagemementTools'. Should be either true or false." 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Modules/windows-optional-feature.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | # 9 | # Example: 10 | # 11 | # { 12 | # "paint": [ 13 | # { 14 | # "type": "windows-optional-feature", 15 | # "ensure": "install", 16 | # "all": false, 17 | # "names": [ 18 | # "Microsoft-Hyper-V" 19 | # ] 20 | # } 21 | # ] 22 | # } 23 | ######################################################################### 24 | 25 | # Installs/uninstalled windows optional features 26 | Import-Module $env:PicassioTools -DisableNameChecking -ErrorAction Stop 27 | 28 | function Start-Module($colour, $variables, $credentials) 29 | { 30 | Test-Module $colour $variables $credentials 31 | 32 | $names = $colour.names 33 | $ensure = (Replace-Variables $colour.ensure $variables).ToLower().Trim() 34 | $all = Replace-Variables $colour.all $variables 35 | 36 | ForEach ($name in $names) 37 | { 38 | $name = (Replace-Variables $name $variables).Trim() 39 | Write-Message "`nAttempting to $ensure '$name'." 40 | 41 | switch ($ensure) 42 | { 43 | 'install' 44 | { 45 | if ((Get-WindowsOptionalFeature -Online -FeatureName $name).State -ieq 'enabled') 46 | { 47 | Write-Information ("$name has already been {0}ed." -f $ensure) 48 | continue 49 | } 50 | 51 | if ($all) 52 | { 53 | Enable-WindowsOptionalFeature -Online -NoRestart -FeatureName $name -All 54 | } 55 | else 56 | { 57 | Enable-WindowsOptionalFeature -Online -NoRestart -FeatureName $name 58 | } 59 | 60 | if (!$?) 61 | { 62 | throw 'Failed to install Windows optional feature.' 63 | } 64 | } 65 | 66 | 'uninstall' 67 | { 68 | if ((Get-WindowsOptionalFeature -Online -FeatureName $name).State -ieq 'disabled') 69 | { 70 | Write-Information ("$name has already been {0}ed." -f $ensure) 71 | continue 72 | } 73 | 74 | Disable-WindowsOptionalFeature -Online -NoRestart -FeatureName $name 75 | 76 | if (!$?) 77 | { 78 | throw 'Failed to uninstall Windows optional feature.' 79 | } 80 | } 81 | } 82 | 83 | Write-Message "$ensure of '$name' successful." 84 | } 85 | 86 | Write-Information 'It is suggested that you restart your computer.' 87 | } 88 | 89 | function Test-Module($colour, $variables, $credentials) 90 | { 91 | # Check feature names are all valid 92 | $names = $colour.names 93 | if ($names -eq $null -or $names.Length -eq 0) 94 | { 95 | throw 'No Windows optional feature names have been supplied.' 96 | } 97 | 98 | ForEach ($name in $names) 99 | { 100 | $name = Replace-Variables $name $variables 101 | 102 | if ([string]::IsNullOrEmpty($name) -or (Get-WindowsOptionalFeature -Online -FeatureName $name | Measure-Object).Count -eq 0) 103 | { 104 | throw "Invalid Windows optional feature: '$name'." 105 | } 106 | } 107 | 108 | # Check ensures value 109 | $ensure = Replace-Variables $colour.ensure $variables 110 | $ensures = @('install', 'uninstall') 111 | if ([string]::IsNullOrWhiteSpace($ensure) -or $ensures -inotcontains ($ensure.Trim())) 112 | { 113 | throw ("Invalid ensure found: '$ensure'. Can be only: {0}." -f ($ensures -join ', ')) 114 | } 115 | 116 | # Check we want to install all features 117 | $all = Replace-Variables $colour.all $variables 118 | if (![string]::IsNullOrWhiteSpace($all) -and $all -ne $true -and $all -ne $false) 119 | { 120 | throw "Invalid value for all: '$all'. Should be either true or false." 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Picassio.pssproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | 2.0 6 | 6CAFC0C6-A428-4d30-A9F9-700E829FEA51 7 | Exe 8 | MyApplication 9 | MyApplication 10 | Picassio 11 | 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug\ 18 | DEBUG;TRACE 19 | prompt 20 | 4 21 | 22 | 23 | pdbonly 24 | true 25 | bin\Release\ 26 | TRACE 27 | prompt 28 | 4 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/Picassio.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "Picassio", "Picassio.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /src/Tools/PicassioTools.psm1: -------------------------------------------------------------------------------- 1 | ########################################################################## 2 | # Picassio is a provisioning/deployment script which uses a single linear 3 | # JSON file to determine what commands to execute. 4 | # 5 | # Copyright (c) 2015, Matthew Kelly (Badgerati) 6 | # Company: Cadaeic Studios 7 | # License: MIT (see LICENSE for details) 8 | ######################################################################### 9 | 10 | # Tools for which to utilise in Picassio modules and extensions 11 | 12 | # Returns the current version of Picassio 13 | function Get-Version() 14 | { 15 | return '$version$' 16 | } 17 | 18 | # Returns the current version of PowerShell 19 | function Get-PowerShellVersion() 20 | { 21 | try 22 | { 23 | return [decimal]([string](Get-Host | Select-Object Version).Version) 24 | } 25 | catch 26 | { 27 | return [decimal]((Get-Host).Version.Major) 28 | } 29 | } 30 | 31 | # Checks to see if the user has administrator priviledges 32 | function Test-AdminUser() 33 | { 34 | try 35 | { 36 | $principal = New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent()) 37 | 38 | if ($principal -eq $null) 39 | { 40 | return $false 41 | } 42 | 43 | return $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) 44 | } 45 | catch [exception] 46 | { 47 | Write-Host 'Error checking user administrator priviledges' 48 | Write-Host $_.Exception.Message -ForegroundColor Red 49 | return $false 50 | } 51 | } 52 | 53 | # Writes the help manual to the console 54 | function Write-Help() 55 | { 56 | Write-Host 'Help Manual' -ForegroundColor Green 57 | Write-Host ([string]::Empty) 58 | 59 | if (!(Test-PicassioInstalled)) 60 | { 61 | Write-Host 'To install Picassio use: ".\Picassio.ps1 -install"' -ForegroundColor Yellow 62 | Write-Host ([string]::Empty) 63 | } 64 | 65 | Write-Host 'The following is a list of possible commands:' 66 | Write-Host " -help`t`t Displays the help page" 67 | Write-Host " -voice`t`t Enables voice over for some textual output" 68 | Write-Host " -validate`t Validates the palette" 69 | Write-Host " -install`t Installs/Updates Picassio, extensions are kept intact" 70 | Write-Host " -uninstall`t Uninstalls Picassio" 71 | Write-Host " -reinstall`t Uninstalls and then re-installs Picassio" 72 | Write-Host " -version`t Displays the current version of Picassio" 73 | Write-Host " -palette`t Specifies the picassio palette file to use (Default: picassio.palette)" 74 | Write-Host " -paint`t`t Runs the palette file's paint section" 75 | Write-Host " -erase`t`t Runs the palette file's erase section, if one is present" 76 | Write-Host " -username`t [Optional] Your username to use for initial credentials" 77 | Write-Host " -password`t [Optional] Your password, this can be left blank" 78 | Write-Host " -force`t`t Forces Picassio to run, regardless of the user having administrator priviledges" 79 | Write-Host ([string]::Empty) 80 | } 81 | 82 | # Writes the current version of Picassio to the console 83 | function Write-Version() 84 | { 85 | $version = Get-Version 86 | $psVersion = Get-PowerShellVersion 87 | Write-Host "Picassio v$version (PS $psVersion)" -ForegroundColor Green 88 | } 89 | 90 | # Wipes a given directory 91 | function Remove-Directory($directory) 92 | { 93 | Write-Message "Removing directory: '$directory'." 94 | 95 | Remove-Item -Recurse -Force $directory | Out-Null 96 | if (!$?) 97 | { 98 | throw 'Failed to remove directory.' 99 | } 100 | 101 | Write-Message 'Directory removed successfully.' 102 | } 103 | 104 | # Backs up a directory, appending the current date/time 105 | function Backup-Directory($directory) 106 | { 107 | Write-Message "Backing-up directory: '$directory'" 108 | $newDir = $directory + '_' + ([DateTime]::Now.ToString('yyyy-MM-dd_HH-mm-ss')) 109 | 110 | Rename-Item $directory $newDir -Force | Out-Null 111 | if (!$?) 112 | { 113 | throw 'Failed to backup directory.' 114 | } 115 | 116 | Write-Message "Directory '$directory' renamed to '$newDir'" 117 | } 118 | 119 | # Returns whether Picassio has been installed or not 120 | function Test-PicassioInstalled() 121 | { 122 | return !([String]::IsNullOrWhiteSpace($env:PicassioTools) -or [String]::IsNullOrWhiteSpace($env:PicassioModules) -or [String]::IsNullOrWhiteSpace($env:PicassioExtensions)) 123 | } 124 | 125 | # Writes a general message to the console (cyan) 126 | function Write-Message([string]$message, $speech = $null) 127 | { 128 | Write-Host $message -ForegroundColor Cyan 129 | Speak-Text $message $speech 130 | } 131 | 132 | # Writes a new line to the console 133 | function Write-NewLine() 134 | { 135 | Write-Host ([string]::Empty) 136 | } 137 | 138 | # Writes error text to the console (red) 139 | function Write-Errors([string]$message, $speech = $null, [switch]$tag) 140 | { 141 | if ($tag) 142 | { 143 | $message = "[ERROR] $message" 144 | } 145 | 146 | Write-Host $message -ForegroundColor Red 147 | Speak-Text $message $speech 148 | } 149 | 150 | # Writes the exception to the console (red) 151 | function Write-Exception($exception) 152 | { 153 | Write-Host $exception.GetType().FullName -ForegroundColor Red 154 | Write-Host $exception.Message -ForegroundColor Red 155 | } 156 | 157 | # Write informative text to the console (green) 158 | function Write-Information([string]$message, $speech = $null) 159 | { 160 | Write-Host $message -ForegroundColor Green 161 | Speak-Text $message $speech 162 | } 163 | 164 | # Write a stamp message to the console (magenta) 165 | function Write-Stamp([string]$message, $speech = $null) 166 | { 167 | Write-Host $message -ForegroundColor Magenta 168 | Speak-Text $message $speech 169 | } 170 | 171 | # Writes a warning to the console (yellow) 172 | function Write-Warnings([string]$message, $speech = $null, [switch]$tag) 173 | { 174 | if ($tag) 175 | { 176 | $message = "[WARNING] $message" 177 | } 178 | 179 | Write-Host $message -ForegroundColor Yellow 180 | Speak-Text $message $speech 181 | } 182 | 183 | # Writes a header to the console in uppercase (magenta) 184 | function Write-Header([string]$message) 185 | { 186 | if ($message -eq $null) 187 | { 188 | $message = [string]::Empty 189 | } 190 | 191 | $count = 65 192 | $message = $message.ToUpper() 193 | 194 | if ($message.Length -gt $count) 195 | { 196 | Write-Host "$message>" -ForegroundColor Magenta 197 | } 198 | else 199 | { 200 | $length = $count - $message.Length 201 | $padding = ('=' * $length) 202 | Write-Host "=$message$padding>" -ForegroundColor Magenta 203 | } 204 | } 205 | 206 | # Writes a sub-header to the console in uppercase (dark yellow) 207 | function Write-SubHeader([string]$message) 208 | { 209 | if ($message -eq $null) 210 | { 211 | $message = [string]::Empty 212 | } 213 | 214 | $count = 65 215 | $message = $message.ToUpper() 216 | 217 | if ($message.Length -gt $count) 218 | { 219 | Write-Host "$message>" -ForegroundColor DarkYellow 220 | } 221 | else 222 | { 223 | $length = $count - $message.Length 224 | $padding = ('-' * $length) 225 | Write-Host "-$message$padding>" -ForegroundColor DarkYellow 226 | } 227 | } 228 | 229 | # Speaks the passed text using the passed speech object 230 | function Speak-Text([string]$text, $speech) 231 | { 232 | if ($speech -eq $null -or [string]::IsNullOrWhiteSpace($text)) 233 | { 234 | return 235 | } 236 | 237 | try 238 | { 239 | $speech.SpeakAsync($text) | Out-Null 240 | } 241 | catch { } 242 | } 243 | 244 | # Read input for the user, to check if they are sure about something (asks y/n) 245 | function Read-AreYouSure() 246 | { 247 | $valid = @('y', 'n') 248 | $value = Read-Host -Prompt "Are you sure you wish to continue? y/n" 249 | 250 | if ([string]::IsNullOrWhiteSpace($value) -or $valid -inotcontains $value) 251 | { 252 | return Read-AreYouSure 253 | } 254 | 255 | return $value -ieq 'y' 256 | } 257 | 258 | 259 | # Resets the PATH for the current session 260 | function Reset-Path($showMessage = $true) 261 | { 262 | $env:Path = (Get-EnvironmentVariable 'Path') + ';' + (Get-EnvironmentVariable 'Path' 'User') 263 | 264 | if ($showMessage) 265 | { 266 | Write-Message 'Path updated.' 267 | } 268 | } 269 | 270 | # Check to see if a piece of software is installed 271 | function Test-Software($command, $name = $null) 272 | { 273 | try 274 | { 275 | # attempt to see if it's install via chocolatey 276 | if (![String]::IsNullOrWhiteSpace($name)) 277 | { 278 | try 279 | { 280 | $result = (choco.exe list -lo | Where-Object { $_ -ilike "*$name*" } | Measure-Object).Count 281 | } 282 | catch [exception] 283 | { 284 | $result = 0 285 | } 286 | 287 | if ($result -ne 0) 288 | { 289 | return $true 290 | } 291 | } 292 | 293 | # attempt to call the program, see if we get a response back 294 | $output = powershell.exe /C "$command" | Out-Null 295 | if ($LASTEXITCODE -ne 0) 296 | { 297 | return $false 298 | } 299 | 300 | return $true 301 | } 302 | catch [exception] { } 303 | 304 | return $false 305 | } 306 | 307 | # Tests to see if a URL is valid 308 | function Test-Url($url) 309 | { 310 | if ([string]::IsNullOrWhiteSpace($url)) 311 | { 312 | return $false 313 | } 314 | 315 | try 316 | { 317 | $request = [System.Net.WebRequest]::Create($url) 318 | $response = $request.GetResponse() 319 | return $response.StatusCode -eq 200 320 | } 321 | catch [exception] 322 | { 323 | return $false 324 | } 325 | finally 326 | { 327 | if ($response -ne $null) 328 | { 329 | $response.Close() 330 | } 331 | } 332 | } 333 | 334 | # Installs software via Chocolatey on an adhoc basis 335 | function Install-AdhocSoftware($packageName, $name, $installer = 'choco') 336 | { 337 | Write-Message "Installing $name." 338 | 339 | if ([string]::IsNullOrWhiteSpace($packageName)) 340 | { 341 | throw 'Package name for installing adhoc software cannot be empty.' 342 | } 343 | 344 | switch ($installer) 345 | { 346 | 'choco' 347 | { 348 | if (!(Test-Software 'choco -v')) 349 | { 350 | Write-Warnings 'Chocolatey is not installed' 351 | Install-Chocolatey 352 | } 353 | 354 | Run-Command 'choco.exe' "install $packageName -y" 355 | } 356 | 357 | 'npm' 358 | { 359 | if (!(Test-Software 'node.exe -v' 'nodejs')) 360 | { 361 | Write-Warnings 'node.js is not installed' 362 | Install-AdhocSoftware 'nodejs.install' 'node.js' 363 | } 364 | 365 | Run-Command 'npm' "install -g $packageName" $false $true 366 | } 367 | 368 | default 369 | { 370 | throw "Invalid installer type found for adhoc software: '$installer'." 371 | } 372 | } 373 | 374 | # Was the install successful 375 | if (!$?) 376 | { 377 | throw "Failed to install $name." 378 | } 379 | 380 | Reset-Path $false 381 | Write-Message "Installation of $name successful." 382 | } 383 | 384 | # Install Chocolatey - if already installed, will just update 385 | function Install-Chocolatey() 386 | { 387 | if (Test-Software 'choco -v') 388 | { 389 | Write-Message 'Chocolatey is already installed.' 390 | return 391 | } 392 | 393 | Write-Message 'Installing Chocolately.' 394 | 395 | $policies = @('Unrestricted', 'ByPass') 396 | if ($policies -inotcontains (Get-ExecutionPolicy)) 397 | { 398 | Set-ExecutionPolicy Bypass -Force 399 | } 400 | 401 | Run-Command "Invoke-Expression ((New-Object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" $null $true $true 402 | Write-Message 'Chocolately installed.' 403 | 404 | Reset-Path 405 | } 406 | 407 | # Check to see if we have a paint colour of the passed type 408 | function Test-ColourType($json, $type) 409 | { 410 | $list = [array]($json.palette.paint | Where-Object { $_.type.ToLower() -eq $type.ToLower() }) 411 | return ($list.Length -ne 0) 412 | } 413 | 414 | # Return an environment variable 415 | function Get-EnvironmentVariable($name, $level = 'Machine') 416 | { 417 | $value = [Environment]::GetEnvironmentVariable($name, $level) 418 | 419 | if (!$?) 420 | { 421 | throw "Failed to get environment variable '$name' from '$level' level." 422 | } 423 | 424 | return $value 425 | } 426 | 427 | # Sets an environment variable 428 | function Set-EnvironmentVariable($name, $value, $level = 'Machine') 429 | { 430 | [Environment]::SetEnvironmentVariable($name, $value, $level) 431 | 432 | if (!$?) 433 | { 434 | throw "Failed to set environment variable '$name' at '$level' level." 435 | } 436 | } 437 | 438 | # Tests whether the current shell is open in a 32-bit host 439 | function Test-Win32() 440 | { 441 | return [IntPtr]::Size -eq 4; 442 | } 443 | 444 | # Tests whether the current shell is open in a 64-bit host 445 | function Test-Win64() 446 | { 447 | return [IntPtr]::Size -eq 8; 448 | } 449 | 450 | # Runs the passed command and arguments. If fails displays the last 200 lines of output 451 | function Run-Command([string]$command, [string]$_args, [bool]$fullOutput = $false, [bool]$isPowershell = $false, [bool]$ignoreFailure = $false) 452 | { 453 | Write-Information "Running command: '$command $_args'" 454 | 455 | if ($ignoreFailure) 456 | { 457 | Write-Warnings 'Failures are being suppressed' -tag 458 | } 459 | 460 | if ($isPowershell) 461 | { 462 | $output = powershell.exe /C "`"$command`" $_args" 463 | 464 | if (!$? -and !$ignoreFailure) 465 | { 466 | if ($output -ne $null) 467 | { 468 | if (!$fullOutput) 469 | { 470 | $output = ($output | Select-Object -Last 200) 471 | } 472 | 473 | $output | ForEach-Object { Write-Errors $_ } 474 | } 475 | 476 | throw "Command '$command' failed to complete." 477 | } 478 | } 479 | else 480 | { 481 | $output = cmd.exe /C "`"$command`" $_args" 482 | $code = $LASTEXITCODE 483 | 484 | if ($code -ne 0 -and !$ignoreFailure) 485 | { 486 | if ($output -ne $null) 487 | { 488 | if (!$fullOutput) 489 | { 490 | $output = ($output | Select-Object -Last 200) 491 | } 492 | 493 | $output | ForEach-Object { Write-Errors $_ } 494 | } 495 | 496 | throw "Command '$command' failed to complete. Exit code: $code" 497 | } 498 | } 499 | } 500 | 501 | # Returns the regex for variables 502 | function Get-VariableRegex() 503 | { 504 | return '(?[a-zA-Z0-9_]+)' 505 | } 506 | 507 | # Replaces a passed value with variable substitutes 508 | function Replace-Variables($value, $variables) 509 | { 510 | if ($variables -eq $null -or $variables.Count -eq 0 -or [string]::IsNullOrWhiteSpace($value)) 511 | { 512 | return $value 513 | } 514 | 515 | $varregex = Get-VariableRegex 516 | $pattern = "#\($varregex\)" 517 | $varnames = ($value | Select-String -Pattern $pattern -AllMatches | ForEach-Object { $_.Matches } | ForEach-Object { $_.Groups['var'].Value }) 518 | 519 | if ($varnames -eq $null -or $varnames.Count -eq 0) 520 | { 521 | return $value 522 | } 523 | 524 | ForEach ($varname in $varnames) 525 | { 526 | if (!$variables.ContainsKey($varname)) 527 | { 528 | continue 529 | } 530 | 531 | $val = $variables[$varname] 532 | $var = "#\($varname\)" 533 | $value = ($value -replace $var, $val) 534 | } 535 | 536 | return $value 537 | } 538 | -------------------------------------------------------------------------------- /tests/bower.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "directory", 6 | "ensure": "exists", 7 | "path": "C:\\Tests\\Bower" 8 | }, 9 | { 10 | "type": "bower", 11 | "ensure": "install", 12 | "path": "C:\\Tests\\Bower", 13 | "packages": { 14 | "jquery": "1.8.2", 15 | "normalize.css": "" 16 | } 17 | }, 18 | { 19 | "type": "bower", 20 | "ensure": "install", 21 | "path": "C:\\Tests\\Bower", 22 | "packages": { 23 | "jquery": "" 24 | } 25 | } 26 | ], 27 | "erase": [ 28 | { 29 | "type": "bower", 30 | "ensure": "uninstall", 31 | "path": "C:\\Tests\\Bower", 32 | "packages": { 33 | "jquery": "" 34 | } 35 | }, 36 | { 37 | "type": "directory", 38 | "ensure": "removed", 39 | "path": "C:\\Tests\\Bower" 40 | }, 41 | { 42 | "type": "npm", 43 | "ensure": "uninstall", 44 | "global": true, 45 | "packages": { 46 | "bower": "" 47 | } 48 | }, 49 | { 50 | "type": "chocolatey", 51 | "ensure": "uninstall", 52 | "software": { 53 | "nodejs.install": "", 54 | "git.install": "" 55 | } 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /tests/chef-client.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "chef-client", 6 | "path": ".", 7 | "recipes": [ 8 | "chef-test-recipe.rb" 9 | ] 10 | } 11 | ], 12 | "erase": [ 13 | { 14 | "type": "chocolatey", 15 | "ensure": "uninstall", 16 | "software": { 17 | "chef-client": "" 18 | } 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/chef-test-recipe.rb: -------------------------------------------------------------------------------- 1 | powershell_script 'Test Recipe Script' do 2 | code 'Write-Host "Hello, world!"' 3 | guard_interpreter :powershell_script 4 | end 5 | -------------------------------------------------------------------------------- /tests/chocolatey.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "chocolatey", 6 | "description": "Test to install edison", 7 | "ensure": "install", 8 | "software": { 9 | "edison": "latest" 10 | } 11 | }, 12 | { 13 | "type": "chocolatey", 14 | "description": "Test to attempt to install edison again", 15 | "ensure": "install", 16 | "ignoreFailure": true, 17 | "software": { 18 | "edison": "latest" 19 | } 20 | } 21 | ], 22 | "erase": [ 23 | { 24 | "type": "chocolatey", 25 | "description": "Test to uninstall edison", 26 | "ensure": "uninstall", 27 | "software": { 28 | "edison": "" 29 | } 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tests/command.palette: -------------------------------------------------------------------------------- 1 | { 2 | "paint": [ 3 | { 4 | "type": "command", 5 | "prompt": "powershell", 6 | "command": "Write-Host 'Hello, world!'" 7 | }, 8 | { 9 | "type": "command", 10 | "prompt": "cmd", 11 | "command": "echo Hello, world again!" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tests/directory.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "directory", 6 | "ensure": "exists", 7 | "path": "C:\\Tests\\MkDir\\Test" 8 | } 9 | ], 10 | "erase": [ 11 | { 12 | "type": "directory", 13 | "ensure": "removed", 14 | "path": "C:\\Tests\\MkDir" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /tests/eraseBeforePaint.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseBeforePaint": true, 3 | "eraseAfterPaint": true, 4 | "paint": [ 5 | { 6 | "type": "chocolatey", 7 | "ensure": "install", 8 | "software": { 9 | "edison": "" 10 | } 11 | } 12 | ], 13 | "erase": [ 14 | { 15 | "type": "chocolatey", 16 | "ensure": "uninstall", 17 | "software": { 18 | "edison": "" 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /tests/git.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "directory", 6 | "ensure": "exists", 7 | "path": "C:\\Tests" 8 | }, 9 | { 10 | "type": "chocolatey", 11 | "ensure": "install", 12 | "software": { 13 | "git.install": "" 14 | } 15 | }, 16 | { 17 | "type": "git", 18 | "remote": "https://github.com/Badgerati/Picassio.git", 19 | "path": "C:\\Tests", 20 | "name": "PicassioRenamed", 21 | "branch": "master" 22 | } 23 | ], 24 | "erase": [ 25 | { 26 | "type": "directory", 27 | "ensure": "removed", 28 | "path": "C:\\Tests\\PicassioRenamed" 29 | }, 30 | { 31 | "type": "chocolatey", 32 | "ensure": "uninstall", 33 | "software": { 34 | "git.install": "" 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /tests/ms-sql.palette: -------------------------------------------------------------------------------- 1 | { 2 | "paint": [ 3 | { 4 | "type": "ms-sql", 5 | "connectionString": "server=127.0.0.1;database=Example;Trusted_Connection=True;", 6 | "sql": "DELETE FROM [SomeTable]" 7 | }, 8 | { 9 | "type": "ms-sql", 10 | "connectionString": "server=127.0.0.1;database=Example;Trusted_Connection=True;", 11 | "sqlFile": "C:\\Some\\File.sql" 12 | }, 13 | { 14 | "type": "ms-sql", 15 | "connectionString": "server=127.0.0.1;database=Example;Trusted_Connection=True;", 16 | "sql": "INSERT INTO [SomeTable] VALUES ('value', 1)", 17 | "notIf": "SELECT TOP 1 * FROM [SomeTable] WHERE SomeValue = 'value'" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/npm.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "directory", 6 | "ensure": "exists", 7 | "path": "C:\\Tests\\Npm" 8 | }, 9 | { 10 | "type": "npm", 11 | "ensure": "install", 12 | "path": "C:\\Tests\\Npm", 13 | "packages": { 14 | "express": "@4.13.4", 15 | "mongoose": "" 16 | } 17 | }, 18 | { 19 | "type": "npm", 20 | "ensure": "install", 21 | "path": "C:\\Tests\\Npm", 22 | "packages": { 23 | "express": "" 24 | } 25 | } 26 | ], 27 | "erase": [ 28 | { 29 | "type": "npm", 30 | "ensure": "uninstall", 31 | "path": "C:\\Tests\\Npm", 32 | "packages": { 33 | "express": "" 34 | } 35 | }, 36 | { 37 | "type": "directory", 38 | "ensure": "removed", 39 | "path": "C:\\Tests\\Npm" 40 | }, 41 | { 42 | "type": "chocolatey", 43 | "ensure": "uninstall", 44 | "software": { 45 | "nodejs.install": "" 46 | } 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /tests/picassio.palette: -------------------------------------------------------------------------------- 1 | { 2 | "paint": [ 3 | { 4 | "type": "echo", 5 | "text": "Running Tests" 6 | }, 7 | { 8 | "type": "picassio", 9 | "ensure": "paint", 10 | "palettes": [ 11 | ".\\command.palette", 12 | ".\\chocolatey.palette", 13 | ".\\git.palette", 14 | ".\\directory.palette", 15 | ".\\eraseBeforePaint.palette", 16 | ".\\npm.palette", 17 | ".\\bower.palette" 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/rollbackOnFail.palette: -------------------------------------------------------------------------------- 1 | { 2 | "rollbackOnFail": true, 3 | "eraseAfterPaint": true, 4 | "paint": [ 5 | { 6 | "type": "chocolatey", 7 | "ensure": "install", 8 | "software": { 9 | "edison": "", 10 | "ThisDoesntExist": "" 11 | } 12 | } 13 | ], 14 | "erase": [ 15 | { 16 | "type": "chocolatey", 17 | "ensure": "uninstall", 18 | "software": { 19 | "edison": "" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tests/variables.palette: -------------------------------------------------------------------------------- 1 | { 2 | "paint": [ 3 | { 4 | "type": "variables", 5 | "variables": { 6 | "value1": "world", 7 | "value2": 123 8 | } 9 | }, 10 | { 11 | "type": "echo", 12 | "text": "Hello, #(value1)!" 13 | }, 14 | { 15 | "type": "echo", 16 | "text": "Example of a number: #(value2)" 17 | }, 18 | { 19 | "type": "echo", 20 | "text": "Using two variables: Hello, #(value1) and #(value2)" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /tests/windows-feature.palette: -------------------------------------------------------------------------------- 1 | { 2 | "eraseAfterPaint": true, 3 | "paint": [ 4 | { 5 | "type": "windows-feature", 6 | "ensure": "install", 7 | "name": "Web-Server", 8 | "includeSubFeatures": true, 9 | "includeManagementTools": true 10 | } 11 | ], 12 | "erase": [ 13 | { 14 | "type": "windows-feature", 15 | "ensure": "uninstall", 16 | "name": "Web-Server", 17 | "includeManagementTools": true 18 | } 19 | ] 20 | } 21 | --------------------------------------------------------------------------------