├── .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 | [](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 |
--------------------------------------------------------------------------------