├── .gitattributes
├── .gitignore
├── Apps
├── Apps.sln
└── Apps
│ ├── Apps.pssproj
│ ├── Deploy-SPApp.Tests.ps1
│ ├── Deploy-SPApp.ps1
│ ├── Deploy-WebSite.ps1
│ ├── Install-SPAddIn
│ ├── Install-SPAddIn.Tests.ps1
│ └── Install-SPAddIn.ps1
│ ├── New-AppPackage
│ ├── New-AppPackage.Tests.ps1
│ └── New-AppPackage.ps1
│ ├── Replace-Tokens.ps1
│ ├── Set-AssemblyVersion.Tests.ps1
│ ├── Set-AssemblyVersion.ps1
│ └── Trust-SPApp.ps1
├── DSCPublishing
├── DSCResourcesConfiguration.ps1
├── Ensure-DSCResources.ps1
├── Package.ps1
├── README.md
└── TFSBuild.proj
├── DevOps
├── DevOps.sln
└── DevOps
│ ├── Backup-Database.ps1
│ ├── Backup-Website.ps1
│ ├── DevOps.pssproj
│ ├── Get-TFSRelease.ps1
│ ├── Get-VSTSRelease.ps1
│ └── Restore-Website.ps1
├── README.md
├── Set-RepositoryPermission
├── Readme.md
└── Set-RepositoryPermission.ps1
├── TFS
└── ReleaseVariables
│ ├── Get-ReleasesWithVariable.ps1
│ ├── Readme.md
│ └── Update-ReleaseVariables.ps1
└── TFSBuild
├── README.md
├── TFSBuild.sln
└── TFSBuild
├── AssemblyVersion
├── AssemblyVersion.pssproj
├── README.md
└── Set-AssemblyVersion
│ ├── Set-AssemblyVersion.Tests.ps1
│ └── Set-AssemblyVersion.ps1
└── Start-Release
└── Start-Release.ps1
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.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 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
184 |
--------------------------------------------------------------------------------
/Apps/Apps.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "Apps", "Apps\Apps.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 |
--------------------------------------------------------------------------------
/Apps/Apps/Apps.pssproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | 6CAFC0C6-A428-4d30-A9F9-700E829FEA51
7 | Exe
8 | MyApplication
9 | MyApplication
10 | Apps
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug\
17 | DEBUG;TRACE
18 | prompt
19 | 4
20 |
21 |
22 | pdbonly
23 | true
24 | bin\Release\
25 | TRACE
26 | prompt
27 | 4
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Apps/Apps/Deploy-SPApp.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
3 | . "$here\$sut"
4 |
5 | Describe "Deploy-SPApp" {
6 | It "does something useful" {
7 | $true | Should Be $false
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Apps/Apps/Deploy-SPApp.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
2 | [OutputType([int])]
3 | Param
4 | (
5 | [Parameter(Mandatory=$true, Position=0)]
6 | [ValidateNotNullOrEmpty()]
7 | [string]$AppName,
8 |
9 | [Parameter(Mandatory=$true, Position=1)]
10 | [ValidateNotNullOrEmpty()]
11 | [string]$WebUrl,
12 |
13 | [Parameter(Mandatory=$true, Position=2)]
14 | [ValidateNotNullOrEmpty()]
15 | [string]$RemoteAppUrl,
16 |
17 | [Parameter(Mandatory=$true, Position=3)]
18 | [ValidateNotNullOrEmpty()]
19 | [string]$DeployUserName,
20 |
21 | [Parameter(Mandatory=$true, Position=4)]
22 | [ValidateNotNullOrEmpty()]
23 | [string]$DeployPassword
24 | )
25 |
26 | function Install-App($clientContext, $appPackage, $productId) {
27 | $appName = [System.IO.Path]::GetFileNameWithoutExtension($appPackage)
28 | $web = $clientContext.Web
29 |
30 | Write-Verbose "Start to install app $appName..."
31 |
32 | # Try to uninstall any existing app instances first.
33 | Uninstall-App $clientContext $productId
34 |
35 | Write-Verbose "Installing app $appName..."
36 | $appInstance = $web.LoadAndInstallAppInSpecifiedLocale(([System.IO.FileInfo]$appPackage).OpenRead(), $web.Language)
37 | $clientContext.Load($appInstance)
38 | $clientContext.ExecuteQuery()
39 |
40 | $appInstance = WaitForAppOperationComplete $clientContext $appInstance.Id
41 |
42 | if (!$appInstance -Or $appInstance.Status -ne [Microsoft.SharePoint.Client.AppInstanceStatus]::Installed)
43 | {
44 | if ($appInstance -And $appInstance.Id)
45 | {
46 | Write-Error "App installation failed. To check app details, go to '$($web.Url.TrimEnd('/'))/_layouts/15/AppMonitoringDetails.aspx?AppInstanceId=$($appInstance.Id)'."
47 | }
48 |
49 | throw "App installation failed."
50 | }
51 |
52 | return $appInstance.Id
53 | }
54 |
55 | function Uninstall-App($clientContext, $productId) {
56 | $appInstances = $web.GetAppInstancesByProductId($productId)
57 | $clientContext.Load($appInstances)
58 | $clientContext.ExecuteQuery()
59 |
60 | if ($appInstances -And $appInstances.Length -gt 0)
61 | {
62 | $appInstance = $appInstances[0]
63 |
64 | Write-Verbose "Uninstalling app with instance id $($appInstance.Id)..."
65 | $appInstance.Uninstall() | out-null
66 | $clientContext.Load($appInstance)
67 | $clientContext.ExecuteQuery()
68 |
69 | $appInstance = WaitForAppOperationComplete $clientContext $appInstance.Id
70 |
71 | # Assume the app uninstallation succeeded
72 | Write-Verbose "App was uninstalled successfully."
73 | }
74 | }
75 |
76 | function WaitForAppOperationComplete($clientContext, $appInstanceId) {
77 |
78 | for ($i = 0; $i -le 2000; $i++)
79 | {
80 | try
81 | {
82 | $web = $clientContext.Web
83 | $instance = $web.GetAppInstanceById($appInstanceId)
84 | $clientContext.Load($instance)
85 | $clientContext.ExecuteQuery()
86 | }
87 | catch [Microsoft.SharePoint.Client.ServerException]
88 | {
89 | # When the uninstall finished, "app is not found" server exception will be thrown.
90 | # Assume the uninstalling operation succeeded.
91 | break
92 | }
93 |
94 | if (!$instance)
95 | {
96 | break
97 | }
98 |
99 | $result = $instance.Status;
100 | if ($result -ne [Microsoft.SharePoint.Client.AppInstanceStatus]::Installed -And
101 | !$instance.InError -And
102 | # If an app has failed to install correctly, it would return to initialized state if auto-cancel was enabled
103 | $result -ne [Microsoft.SharePoint.Client.AppInstanceStatus]::Initialized)
104 | {
105 | Write-Verbose "Instance status: $result"
106 | Start-Sleep -m 1000
107 | }
108 | else
109 | {
110 | break
111 | }
112 | }
113 |
114 | return $instance;
115 | }
116 |
117 | function Enable-SideLoading {
118 |
119 | param($clientContext, $enable = $true, $force = $false)
120 |
121 | # this is the side-loading Feature ID..
122 | $FeatureId = [GUID]("AE3A1339-61F5-4f8f-81A7-ABD2DA956A7D")
123 |
124 | # ..and this one is site-scoped, so using $clientContext.Site.Features..
125 | $siteFeatures = $clientContext.Site.Features
126 | $clientContext.Load($siteFeatures)
127 | $clientContext.ExecuteQuery()
128 |
129 | $feature = $siteFeatures | Where-Object { $_.DefinitionId -eq $FeatureId } | Select-Object -First 1
130 |
131 | if ($feature)
132 | {
133 | if ($enable)
134 | {
135 | Write-Verbose "Feature is already activated in this site."
136 | return
137 | }
138 | else
139 | {
140 | $siteFeatures.Remove($featureId, $force)
141 | }
142 | }
143 | else
144 | {
145 | if ($enable)
146 | {
147 | $siteFeatures.Add($featureId, $force, [Microsoft.SharePoint.Client.FeatureDefinitionScope]::None)
148 | }
149 | else
150 | {
151 | Write-Verbose "The feature is not active at this scope."
152 | return
153 | }
154 | }
155 |
156 | try
157 | {
158 | $clientContext.ExecuteQuery()
159 | if ($enable)
160 | {
161 | Write-Verbose "Feature '$FeatureId' successfully activated.."
162 | }
163 | else
164 | {
165 | Write-Verbose "Feature '$FeatureId' successfully deactivated.."
166 | }
167 | }
168 | catch
169 | {
170 | throw "An error occurred whilst activating/deactivating the Feature. Error detail: $($_)"
171 | }
172 | }
173 |
174 | function Load-Assemblies {
175 |
176 | # suppress output
177 | $assembly1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
178 | $assembly2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
179 |
180 | Write-Verbose "Assemblies loaded."
181 | }
182 |
183 | function WaitFor-IEReady {
184 |
185 | [CmdletBinding()]
186 | Param
187 | (
188 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
189 | $ie,
190 |
191 | [Parameter(Mandatory=$false, Position=1)]
192 | $initialWaitInSeconds = 1
193 | )
194 |
195 | sleep -Seconds $initialWaitInSeconds
196 |
197 | while ($ie.Busy) {
198 |
199 | sleep -milliseconds 50
200 | }
201 | }
202 |
203 | <#
204 | .Synopsis
205 | Invoke a java script function.
206 | .DESCRIPTION
207 | Use this function to run JavaScript on a web page. Your $Command can
208 | return a value which will be returned by this function unless $global
209 | switch is specified in which case $Command will be executed in global
210 | scope and cannot return a value. If you received error 80020101 it means
211 | you need to fix your JavaScript code.
212 | .EXAMPLE
213 | Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);'
214 | .EXAMPLE
215 | $result = Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);' -Global
216 | #>
217 | function Invoke-JavaScript {
218 |
219 | [CmdletBinding()]
220 | [OutputType([string])]
221 | Param
222 | (
223 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
224 | $IE,
225 |
226 | [Parameter(Mandatory=$true, Position=1)]
227 | $Command,
228 |
229 | [switch]$Global
230 | )
231 |
232 | if (-not $Global.IsPresent) {
233 | $Command = "document.body.setAttribute('PSResult', (function(){ $Command })());"
234 | }
235 |
236 | $document = $IE.document
237 | $window = $document.parentWindow
238 | $window.execScript($Command, 'javascript') | Out-Null
239 |
240 | if (-not $Global.IsPresent) {
241 | return $document.body.getAttribute('PSResult')
242 | }
243 | }
244 |
245 | function Get-AppPackageInformations {
246 |
247 | [CmdletBinding()]
248 | [OutputType([Hashtable])]
249 | param([string]$Path, [string]$AppWebUrl)
250 |
251 | # Open zip
252 | Add-Type -assembly System.IO.Compression.FileSystem
253 | Write-Verbose "Open zip file '$Path'..."
254 | $zip = [System.IO.Compression.ZipFile]::Open($Path, "Update")
255 |
256 | try{
257 | $fileToEdit = "AppManifest.xml"
258 | $file = $zip.Entries.Where({$_.name -eq $fileToEdit})
259 |
260 | Write-Verbose "Read app manifest from '$file'."
261 | $desiredFile = [System.IO.StreamReader]($file).Open()
262 | [xml]$xml = $desiredFile.ReadToEnd()
263 | $desiredFile.Close()
264 |
265 | if ($env:ClientId){
266 | $clientId = $env:ClientId
267 | }else{
268 | $clientId = $xml.App.AppPrincipal.RemoteWebApplication.ClientId
269 | }
270 |
271 | $appInformations = @{
272 | ClientId = $clientId
273 | Version = $xml.App.Version
274 | AllowAppOnlyPolicy = [bool]$xml.App.AppPermissionRequests.AllowAppOnlyPolicy
275 | Title = $xml.App.Properties.Title
276 | ProductID = $xml.App.ProductID
277 | }
278 |
279 | if ($AppWebUrl){
280 | # Replace URL
281 | $value = $xml.App.Properties.StartPage
282 | Write-Verbose "Replace URL in '$value' with '$AppWebUrl'."
283 | $value = $value -replace "^.*\?","$($AppWebUrl)?"
284 | $xml.App.Properties.StartPage = $value
285 |
286 | if ($env:ClientId){
287 | Write-Verbose "Found ClientId '$env:ClientId' in environment. Replace it in app."
288 | $xml.App.AppPrincipal.RemoteWebApplication.ClientId = $env:ClientId
289 | }
290 |
291 | # Save file
292 | Write-Verbose "Save manifest to '$file'."
293 | $desiredFile = [System.IO.Stream]($file).Open()
294 | $desiredFile.SetLength(0)
295 | $xml.Save($desiredFile)
296 | $desiredFile.Flush()
297 | $desiredFile.Close()
298 | $desiredFile.Dispose()
299 |
300 | $desiredFile = [System.IO.StreamReader]($file).Open()
301 | [xml]$xml = $desiredFile.ReadToEnd()
302 | Write-Host $xml
303 | $desiredFile.Close()
304 | $desiredFile.Dispose()
305 | }
306 |
307 | return $appInformations
308 |
309 | }finally{
310 | # Write the changes and close the zip file
311 | $zip.Dispose()
312 | }
313 | }
314 |
315 | function WaitFor-IEReady {
316 |
317 | [CmdletBinding()]
318 | Param
319 | (
320 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
321 | $ie,
322 |
323 | [Parameter(Mandatory=$false, Position=1)]
324 | $initialWaitInSeconds = 1
325 | )
326 |
327 | sleep -Seconds $initialWaitInSeconds
328 |
329 | while ($ie.Busy) {
330 |
331 | sleep -milliseconds 50
332 | }
333 | }
334 |
335 | <#
336 | .Synopsis
337 | Invoke a java script function.
338 | .DESCRIPTION
339 | Use this function to run JavaScript on a web page. Your $Command can
340 | return a value which will be returned by this function unless $global
341 | switch is specified in which case $Command will be executed in global
342 | scope and cannot return a value. If you received error 80020101 it means
343 | you need to fix your JavaScript code.
344 | .EXAMPLE
345 | Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);'
346 | .EXAMPLE
347 | $result = Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);' -Global
348 | #>
349 | function Invoke-JavaScript {
350 |
351 | [CmdletBinding()]
352 | [OutputType([string])]
353 | Param
354 | (
355 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
356 | $IE,
357 |
358 | [Parameter(Mandatory=$true, Position=1)]
359 | $Command,
360 |
361 | [switch]$Global
362 | )
363 |
364 | if (-not $Global.IsPresent) {
365 | $Command = "document.body.setAttribute('PSResult', (function(){ $Command })());"
366 | }
367 |
368 | $document = $IE.document
369 | $window = $document.parentWindow
370 | $window.execScript($Command, 'javascript') | Out-Null
371 |
372 | if (-not $Global.IsPresent) {
373 | return $document.body.getAttribute('PSResult')
374 | }
375 | }
376 |
377 | function Trust-SPAddIn {
378 |
379 | [CmdletBinding(SupportsShouldProcess=$true)]
380 | [OutputType([int])]
381 | Param
382 | (
383 | [Parameter(Mandatory=$true, Position=0)]
384 | [guid]$AppInstanceId,
385 |
386 | [Parameter(Mandatory=$true, Position=1)]
387 | [string]$WebUrl,
388 |
389 | [parameter(Mandatory=$true, Position=2)]
390 | [string]$UserName,
391 |
392 | [parameter(Mandatory=$true, Position=3)]
393 | [string]$Password
394 | )
395 |
396 | $authorizeURL = "$($WebUrl.TrimEnd('/'))/_layouts/15/appinv.aspx?AppInstanceId={$AppInstanceId}"
397 |
398 | $ie = New-Object -com internetexplorer.application
399 |
400 | try
401 | {
402 | $ie.Visible = $false
403 | $ie.Navigate2($authorizeURL)
404 |
405 | WaitFor-IEReady $ie
406 |
407 | Write-Verbose $ie.Document.Title -Verbose
408 |
409 | if ($ie.Document.Title -match "Sign in to Office 365.*") {
410 |
411 | Write-Verbose "Authenticate $UserName to O365..."
412 | # Authorize against O365
413 | $useAnotherLink = $ie.Document.getElementById("use_another_account_link")
414 | if ($useAnotherLink) {
415 |
416 | WaitFor-IEReady $ie
417 |
418 | $useAnotherLink.Click()
419 |
420 | WaitFor-IEReady $ie
421 |
422 | }
423 |
424 | $credUseridInputtext = $ie.Document.getElementById("cred_userid_inputtext")
425 | $credUseridInputtext.value = $UserName
426 |
427 | $credPasswordInputtext = $ie.Document.getElementById("cred_password_inputtext")
428 | $credPasswordInputtext.value = $Password
429 |
430 | WaitFor-IEReady $ie
431 |
432 |
433 | # make a jQuery call
434 | $result = Invoke-JavaScript -IE $ie -Command "`nPost.IsSubmitReady();`nsetTimeout(function() {`nPost.SubmitCreds();`n}, 1000);"
435 |
436 | WaitFor-IEReady $ie -initialWaitInSeconds 5
437 |
438 | }
439 |
440 | Write-Verbose $ie.Document.Title -Verbose
441 |
442 |
443 | if ($ie.Document.Title -match "Do you trust.*")
444 | {
445 | sleep -seconds 5
446 |
447 |
448 | $button = $ie.Document.getElementById("ctl00_PlaceHolderMain_BtnAllow")
449 |
450 | if ($button -eq $null) {
451 | $button = $ie.Document.getElementById("ctl00_PlaceHolderMain_LnkRetrust")
452 | }
453 |
454 | if ($button -eq $null) {
455 |
456 | throw "Could not find button to press"
457 |
458 | }else{
459 |
460 | $button.click()
461 |
462 | WaitFor-IEReady $ie
463 |
464 | #if the button press was successful, we should now be on the Site Settings page..
465 | if ($ie.Document.title -like "*trust*") {
466 |
467 | throw "Error: $($ie.Document.body.getElementsByClassName("ms-error").item().InnerText)"
468 |
469 | }else{
470 |
471 | Write-Verbose "App was trusted successfully!"
472 | }
473 | }
474 |
475 | }else{
476 |
477 | throw "Unexpected page '$($ie.LocationName)' was loaded. Please check your url."
478 | }
479 | }
480 | finally
481 | {
482 | $ie.Quit()
483 | }
484 | }
485 |
486 | Write-Host "Look for '$AppName' in '$applicationPath'..."
487 | $appPackage = Get-ChildItem -Path $applicationPath -Recurse -Include $AppName
488 |
489 | if (-not($appPackage)){ throw "No ap ppackage '$AppName' found in '$applicationPath'..." }
490 |
491 | Write-Host "Get package informations and set url in package to '$RemoteAppUrl'"
492 | $appPackageInformations = Get-AppPackageInformations -Path $appPackage.FullName -AppWebUrl $RemoteAppUrl
493 |
494 |
495 | Load-Assemblies
496 |
497 | Write-Host "Connect to '$WebUrl' as '$DeployUserName'..."
498 | $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
499 | $clientContext.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($DeployUserName, (ConvertTo-SecureString $DeployPassword -AsPlainText -Force))
500 |
501 | try
502 | {
503 | $web = $clientContext.Web
504 | $clientContext.Load($web)
505 |
506 | $clientContext.ExecuteQuery();
507 |
508 | Write-Host "Successfully connected to '$WebUrl'..."
509 |
510 | Write-Host "Update SharePoint app '$AppName' in '$WebUrl' to Version '$($appPackageInformations.Version)'..."
511 | $appId = Install-App -clientContext $clientContext -appPackage $appPackage.FullName -productId $appPackageInformations.ProductID
512 | Write-Host "Done."
513 |
514 | Write-Host "Trust the app."
515 | Trust-SPAddIn -AppInstanceId $appId -WebUrl $WebUrl -UserName $DeployUserName -Password $DeployPassword
516 | }
517 | finally
518 | {
519 | $clientContext.Dispose()
520 | }
--------------------------------------------------------------------------------
/Apps/Apps/Deploy-WebSite.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param
3 | (
4 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=0)]
5 | [ValidateNotNullOrEmpty()]
6 | [string]$RootFolder,
7 |
8 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=1)]
9 | [ValidateNotNullOrEmpty()]
10 | [string]$DeployScript
11 | )
12 |
13 |
14 | Write-Verbose "Look for batch '$DeployScript' in '$RootFolder'..."
15 |
16 | $files = Get-ChildItem -Path $RootFolder -Recurse -Filter $DeployScript
17 |
18 | if (-not ($files)) { throw "'$DeployScript' could not be found in '$RootFolder'."}
19 |
20 | if ($files.Count -gt 1) { throw "Found more then one deployment script." }
21 |
22 | $file = $files[0]
23 |
24 | Write-Verbose "Execute '$($file.FullName) /Y /M:https://$($env:WebDeploySiteName).scm.azurewebsites.net:443/msdeploy.axd /u:$($env:AzureUserName) /p:$($env:AzurePassword) /a:Basic'..."
25 |
26 | & $file /Y /M:https://$($env:WebDeploySiteName).scm.azurewebsites.net:443/msdeploy.axd /u:$($env:AzureUserName) /p:$($env:AzurePassword) /a:Basic
27 |
28 |
29 | Write-Verbose "Done."
30 |
31 |
--------------------------------------------------------------------------------
/Apps/Apps/Install-SPAddIn/Install-SPAddIn.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
3 | . "$here\$sut" -AppPackageFullName "lala" -TargetWebFullUrl "http://localhost"
4 |
5 | Describe "Install-SPAddIn" {
6 | context "Given the app package does not exist, it" {
7 | It "throws an excpetion" {
8 | { Install-SPAddIn -AppPackageFullName "C:\lala.app" -TargetWebFullUrl http://localhost } | Should throw "The package 'C:\lala.app' does not exit."
9 | }
10 | }
11 |
12 | context "Given the app package does exist, it" {
13 | In $TestDrive {
14 | Setup -File ".\lala.app"
15 | Mock Install-SPAddInInternal { } -Verifiable
16 |
17 | It "does not throw an exception, and" {
18 | { Install-SPAddIn -AppPackageFullName ".\lala.app" -TargetWebFullUrl http://localhost } | Should not throw
19 | }
20 |
21 | It "installes the package in all sites."{
22 | Install-SPAddIn -AppPackageFullName ".\lala.app" -TargetWebFullUrl @("http://localhost/sites/a", "http://localhost/sites/b")
23 |
24 | Assert-MockCalled Install-SPAddInInternal -Times 2
25 | }
26 | }
27 | }
28 | }
29 |
30 | Describe "Install-SPAddInInternal"{
31 | context "Given the app cannot be installed, it" {
32 | Mock WaitFor-InstallJob { return 'Error' }
33 |
34 | $actual = Install-SPAddInInternal -AppPackageFullName "C:\lala.app" -webUrl http://localhost -sourceApp "ObjectModel" -Whatif -erroraction silentlycontinue
35 |
36 | It "does return one."{
37 | $actual | should be 1
38 | }
39 | }
40 |
41 | context "Given the app is installed, it" {
42 | Mock WaitFor-InstallJob { return 'Installed' }
43 |
44 | $actual = Install-SPAddInInternal -AppPackageFullName "C:\lala.app" -webUrl http://localhost -sourceApp "ObjectModel" -Whatif
45 |
46 | It "returns zero."{
47 | $actual | should be 0
48 | }
49 | }
50 | }
51 |
52 | Describe "WaitFor-InstallJob" {
53 |
54 | Mock Get-ParentSite { }
55 |
56 | context "Given the package cannot be installed, it"{
57 |
58 | Mock Get-AppInstance { return [PSCustomObject]@{ Status = 'Error' } }
59 |
60 | $result = WaitFor-InstallJob -AppInstanceId ([guid]::NewGuid()) -WebUrl "http://localhost"
61 |
62 | It "returns the state returned from SharePoint." {
63 | $result | Should be "Error"
64 | }
65 | }
66 |
67 | context "Given the package cannot be installed in timeout period, it"{
68 |
69 | Mock Get-AppInstance { return [PSCustomObject]@{ Status = 'Installing' } } -Verifiable
70 |
71 | $result = WaitFor-InstallJob -AppInstanceId ([guid]::NewGuid()) -WebUrl "http://localhost"
72 |
73 | It "requeries the status 5 times, and" {
74 | Assert-MockCalled Get-AppInstance -Times 5
75 | }
76 |
77 | It "returns the state from SharePoint." {
78 | $result | Should be "Installing"
79 | }
80 | }
81 |
82 | context "Given the package was installed immediattly, it"{
83 |
84 | Mock Get-AppInstance { return [PSCustomObject]@{ Status = 'Installed' } } -Verifiable
85 |
86 | $result = WaitFor-InstallJob -AppInstanceId ([guid]::NewGuid()) -WebUrl "http://localhost"
87 |
88 | It "queries the app 1 time, and" {
89 | Assert-MockCalled Get-AppInstance -Times 1
90 | }
91 |
92 | It "returns the state 'Installed'." {
93 | $result | Should be "Installed"
94 | }
95 | }
96 |
97 |
98 | context "Given the package was installed after 6 seconds, it" {
99 |
100 | $Global:iteration = 0
101 |
102 | Mock Get-AppInstance {
103 | $Global:iteration++
104 | if ($Global:iteration -lt 3) {
105 | return [PSCustomObject]@{ Status = 'Installing' }
106 | }else{
107 | return [PSCustomObject]@{ Status = 'Installed' }
108 | } } -Verifiable
109 |
110 | $result = WaitFor-InstallJob -AppInstanceId ([guid]::NewGuid()) -WebUrl "http://localhost"
111 |
112 | It "queries the app 3 times, and" {
113 | Assert-MockCalled Get-AppInstance -Times 3
114 | }
115 |
116 | It "returns the state 'Installed'." {
117 | $result | Should be "Installed"
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Apps/Apps/Install-SPAddIn/Install-SPAddIn.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Installs a SharePoint AddIn A.K.A. SharePoint App to one or more specific sites.
4 | .DESCRIPTION
5 | This function installs a SharePoint AddIn A.K.A. SharePoint App to one or more specific sites.
6 | .EXAMPLE
7 | Install-SPAddIn -AppPackageFullName C:\MyCustomAppPackage.app -TargetWebFullUrl http://localhost
8 | .EXAMPLE
9 | Install-SPAddIn -AppPackageFullName C:\MyCustomAppPackage.app -TargetWebFullUrl @("http://localhost/sites/a", "http://localhost/sites/b")
10 | #>
11 | [CmdletBinding(SupportsShouldProcess=$true)]
12 | Param
13 | (
14 | # The full name of the app package (i.e. 'C:\temp\MyCustomAppPackage.app')
15 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
16 | [string]$AppPackageFullName,
17 |
18 | # The urls of the webs youwant to deploy the app to.
19 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=1)]
20 | [string[]]$TargetWebFullUrl,
21 |
22 | # The package source
23 | [Parameter(Mandatory=$false, Position=2)]
24 | [ValidateSet("ObjectModel", "Marketplace", "CorporateCatalog", "DeveloperSite", "RemoteObjectModel")]
25 | [string]$PackageSource = "ObjectModel"
26 | )
27 |
28 | <#
29 | .Synopsis
30 | Installs a SharePoint AddIn A.K.A. SharePoint App to one or more specific sites.
31 | .DESCRIPTION
32 | This function installs a SharePoint AddIn A.K.A. SharePoint App to one or more specific sites.
33 | .EXAMPLE
34 | Install-SPAddIn -AppPackageFullName C:\MyCustomAppPackage.app -TargetWebFullUrl http://localhost
35 | .EXAMPLE
36 | Install-SPAddIn -AppPackageFullName C:\MyCustomAppPackage.app -TargetWebFullUrl @("http://localhost/sites/a", "http://localhost/sites/b")
37 | #>
38 | function Install-SPAddIn
39 | {
40 | [CmdletBinding(SupportsShouldProcess=$true)]
41 | Param
42 | (
43 | # The full name of the app package (i.e. 'C:\temp\MyCustomAppPackage.app')
44 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
45 | [string]$AppPackageFullName,
46 |
47 | # The urls of the webs youwant to deploy the app to.
48 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=1)]
49 | [string[]]$TargetWebFullUrl,
50 |
51 | # The package source
52 | [Parameter(Mandatory=$false, Position=2)]
53 | [ValidateSet("ObjectModel", "Marketplace", "CorporateCatalog", "DeveloperSite", "RemoteObjectModel")]
54 | [string]$PackageSource = "ObjectModel"
55 | )
56 |
57 | Begin
58 | {
59 | if (-not (Test-Path $AppPackageFullName))
60 | {
61 | throw "The package '$AppPackageFullName' does not exit."
62 | }
63 |
64 | Ensure-PSSnapin
65 | $sourceApp = [microsoft.sharepoint.administration.spappsource]$PackageSource
66 | }
67 | Process
68 | {
69 | foreach ($webUrl in $TargetWebFullUrl)
70 | {
71 | Install-SPAddInInternal -AppPackageFullName $AppPackageFullName -webUrl $webUrl -sourceApp $sourceApp
72 | }
73 | }
74 | End
75 | {
76 | Release-PSSnapin
77 | }
78 | }
79 |
80 | function Install-SPAddInInternal
81 | {
82 | [CmdletBinding(SupportsShouldProcess=$true)]
83 | [OutputType([int])]
84 | Param
85 | (
86 | [Parameter(Mandatory=$true, Position=0)]
87 | [string]$AppPackageFullName,
88 |
89 | [Parameter(Mandatory=$true, Position=1)]
90 | [string]$webUrl,
91 |
92 | [Parameter(Mandatory=$true, Position=2)]
93 | [microsoft.sharepoint.administration.spappsource]$sourceApp
94 | )
95 |
96 | Write-Verbose "Start to deploy '$AppPackageFullName' to '$webUrl'..."
97 |
98 | if ($PSCmdlet.ShouldProcess("$webUrl", "Import-SPAppPackage"))
99 | {
100 | $spapp = Import-SPAppPackage -Path "$AppPackageFullName" -Site $webUrl -Source $sourceApp -Confirm:$false -ErrorAction Stop
101 | }
102 |
103 | if ($PSCmdlet.ShouldProcess("$webUrl", "Trust-SPAddIn"))
104 | {
105 | $clientId = Get-ClientIdFromAppPackage $AppPackageFullName
106 | Trust-SPAddIn -ClientId $clientId -WebUrl $webUrl
107 | }
108 |
109 | $appId = [Guid]::Empty;
110 |
111 | if ($PSCmdlet.ShouldProcess("$webUrl", "Install-SPApp"))
112 | {
113 | $app = Install-SPApp -Web $WebUrl -Identity $spapp -Confirm:$false -ErrorAction Stop
114 | $appId = $app.Id
115 | }
116 |
117 | $status = WaitFor-InstallJob -AppInstanceId $appId -WebUrl $webUrl
118 |
119 | if ($status -ne [Microsoft.SharePoint.Administration.SPAppInstanceStatus]::Installed)
120 | {
121 | Write-Error "The app could not be installed. Current state is '$status'."
122 |
123 | $result = 1
124 | }
125 | else
126 | {
127 | $result = 0
128 | }
129 |
130 | Write-Verbose "End deploying '$AppPackageFullName' to '$webUrl'."
131 |
132 | return $result
133 | }
134 |
135 | function Trust-SPAddIn
136 | {
137 | [CmdletBinding(SupportsShouldProcess=$true)]
138 | [OutputType([int])]
139 | Param
140 | (
141 | [Parameter(Mandatory=$true, Position=0)]
142 | [guid]$ClientID,
143 |
144 | [Parameter(Mandatory=$true, Position=1)]
145 | [string]$WebUrl
146 | )
147 |
148 | $authRealm = Get-SPAuthenticationRealm -ServiceContext $WebUrl
149 |
150 | $appIdentifier = $clientID.ToString() + "@" + $authRealm
151 |
152 | $appPrincipal = Get-SPAppPrincipal -Site $WebUrl -NameIdentifier $appIdentifier
153 |
154 | Set-SPAppPrincipalPermission -Site $WebUrl -AppPrincipal $appPrincipal -Scope Site -Right FullControl
155 | }
156 |
157 | function WaitFor-InstallJob
158 | {
159 | [CmdletBinding()]
160 | param
161 | (
162 | [guid]$AppInstanceId,
163 |
164 | [string]$WebUrl
165 | )
166 |
167 | $site = Get-ParentSite -webUrl $WebUrl
168 |
169 | $AppInstance = Get-AppInstance -id $AppInstanceId -site $site
170 |
171 | Write-Verbose "Waiting for app..."
172 |
173 | $i = 0
174 | $max = 5
175 | $sleepTime = 2
176 |
177 | start-sleep -s $sleepTime
178 |
179 | while((($AppInstance.Status -eq [Microsoft.SharePoint.Administration.SPAppInstanceStatus]::Installing) -or ($AppInstance.Status -eq [Microsoft.SharePoint.Administration.SPAppInstanceStatus]::Initialized)) -and ($i -le $max))
180 | {
181 | [int]$complete = ($i / $max) * 100
182 | Write-Progress -Activity "Install app..." -Status "$($complete)% completed" -PercentComplete $complete
183 |
184 | $i++
185 |
186 | start-sleep -s $sleepTime
187 |
188 | $AppInstance = Get-AppInstance -id $AppInstanceId -site $site
189 | }
190 |
191 | Write-Progress -Activity "Install app" -Completed
192 | Write-Verbose "Result of installation is '$($AppInstance.Status)'."
193 |
194 | return $AppInstance.Status
195 | }
196 |
197 | function Get-AppInstance
198 | {
199 | param([guid]$id, $site)
200 |
201 | return Get-SPAppInstance -AppInstanceId $id -Site $site
202 | }
203 |
204 | function Get-ClientIdFromAppPackage
205 | {
206 | [CmdletBinding()]
207 | [OutputType([guid])]
208 | param([string]$Path)
209 |
210 | $folder = [System.IO.Path]::GetDirectoryName($Path)
211 | $tempFolder = "$folder\_tmp"
212 |
213 | mkdir (Join-Path $folder "_tmp") | Out-Null
214 |
215 | try{
216 |
217 | $tempZipFile = "$tempFolder\app.zip"
218 | Copy-Item $Path $tempZipFile
219 |
220 | $shell = New-Object -Com Shell.Application
221 | $zipItem = $shell.NameSpace($tempZipFile)
222 | $appManifest = $zipItem.Items() | Where-Object -Property Name -EQ -Value AppManifest.xml
223 | $shell.NameSpace("$folder\_tmp").CopyHere($appManifest)
224 |
225 | [xml]$xml = Get-Content "$folder\_tmp\AppManifest.xml"
226 |
227 | return $xml.App.AppPrincipal.RemoteWebApplication.ClientId
228 |
229 | }finally{
230 | Remove-Item "$folder\_tmp" -Force -Recurse
231 | }
232 | }
233 |
234 | function Get-ParentSite
235 | {
236 | param([string]$webUrl)
237 |
238 | return (Get-SPWeb $WebUrl).Site
239 | }
240 |
241 | function Write-Message
242 | {
243 | param([string]$message)
244 |
245 | Write-Verbose $message
246 | }
247 |
248 | function Ensure-PSSnapin
249 | {
250 | if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
251 | {
252 | Add-PSSnapin "Microsoft.SharePoint.PowerShell" -Verbose:$false | Out-Null
253 | Write-Verbose "SharePoint Powershell Snapin loaded."
254 | }
255 | }
256 |
257 | function Release-PSSnapin
258 | {
259 | if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -ne $null)
260 | {
261 | Remove-PSSnapin "Microsoft.SharePoint.PowerShell" -Verbose:$false | Out-Null
262 | Write-Verbose "SharePoint Powershell Snapin removed."
263 | }
264 | }
265 |
266 | if (-not $MyInvocation.Line.Contains("`$here\`$sut")){
267 | Install-SPAddIn -AppPackageFullName $AppPackageFullName -TargetWebFullUrl $TargetWebFullUrl -PackageSource $PackageSource
268 | }
--------------------------------------------------------------------------------
/Apps/Apps/New-AppPackage/New-AppPackage.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
3 | . "$here\$sut"
4 |
5 | Describe "New-AppPackage" {
6 | context "Given the SourceDirectory does not exit, it"{
7 | It "throws an exception"{
8 | { New-AppPackage -SourceDirectory "C:\XYZ" } | Should throw
9 | }
10 | }
11 |
12 | context "Given the Binary directory does not exit, it"{
13 | setup -Dir "SourceDir"
14 |
15 | It "creates the directory"{
16 | $actual = New-AppPackage -SourceDirectory "$TestDrive\SourceDir" -BinariesDirectory "$TestDrive\XYZ"
17 | { Test-Path "$TestDrive\XYZ" } | Should Be $true
18 | }
19 | }
20 |
21 | context "Given a single app, it"{
22 | setup -Dir "SourceDir"
23 | setup -Dir "BinariesDir"
24 | setup -File "SourceDir\app.publish\0.0.1.6\MySample.app"
25 |
26 | $actual = New-AppPackage -SourceDirectory "$TestDrive\SourceDir" -BinariesDirectory "$TestDrive\BinariesDir"
27 |
28 | It "copies exactly one app"{
29 | $actual | should be 1
30 | }
31 |
32 | It "copies the app to the binaries directory"{
33 | { Test-Path "$TestDrive\BinariesDir\MySample.app" } | Should Be $true
34 | }
35 | }
36 |
37 | context "Given two apps, it"{
38 | setup -Dir "SourceDir"
39 | setup -Dir "BinariesDir"
40 | setup -File "SourceDir\app1\app.publish\0.0.1.6\MySample1.app"
41 | setup -File "SourceDir\app2\app.publish\0.0.1.6\MySample2.app"
42 |
43 | $actual = New-AppPackage -SourceDirectory "$TestDrive\SourceDir" -BinariesDirectory "$TestDrive\BinariesDir"
44 |
45 | It "copies both apps"{
46 | $actual | should be 2
47 | }
48 | }
49 |
50 | context "Given an app in a different folders then 'app.publish', it"{
51 | setup -Dir "SourceDir"
52 | setup -Dir "BinariesDir"
53 | setup -File "SourceDir\app1\xxxx\0.0.1.6\MySample1.app"
54 |
55 | $actual = New-AppPackage -SourceDirectory "$TestDrive\SourceDir" -BinariesDirectory "$TestDrive\BinariesDir"
56 |
57 | It "ignores the app"{
58 | $actual | should be 0
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Apps/Apps/New-AppPackage/New-AppPackage.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Script that copies the app package to the drop folder.
4 | .DESCRIPTION
5 | This script van be used in a TFS build definition to ensure that an app is created and published to the build folder.
6 | You must use the script together with the build parameters /p:IsPackaging=True
7 | .EXAMPLE
8 | New-AppPackage -SourceDirectory $Env:TF_BUILD_SOURCESDIRECTORY -BinariesDirectory $Env:TF_BUILD_BINARIESDIRECTORY
9 | #>
10 | Param
11 | (
12 | # The source directory
13 | [Parameter(Mandatory=$false, Position=0)]
14 | [string]$SourceDirectory = $Env:TF_BUILD_SOURCESDIRECTORY,
15 |
16 | #The binaries directory
17 | [Parameter(Mandatory=$false, Position=1)]
18 | [string]$BinariesDirectory = $Env:TF_BUILD_BINARIESDIRECTORY
19 | )
20 |
21 |
22 | <#
23 | .Synopsis
24 | Script that copies the app package to the drop folder.
25 | .DESCRIPTION
26 | This script van be used in a TFS build definition to ensure that an app is created and published to the build folder.
27 | You must use the script together with the build parameters /p:IsPackaging=True
28 | .EXAMPLE
29 | New-AppPackage -SourceDirectory $Env:TF_BUILD_SOURCESDIRECTORY -BinariesDirectory $Env:TF_BUILD_BINARIESDIRECTORY
30 | #>
31 | function New-AppPackage
32 | {
33 | [CmdletBinding()]
34 | [OutputType([int])]
35 | Param
36 | (
37 | # The source directory
38 | [Parameter(Mandatory=$false, Position=0)]
39 | [string]$SourceDirectory = $Env:TF_BUILD_SOURCESDIRECTORY,
40 |
41 | #The binaries directory
42 | [Parameter(Mandatory=$false, Position=1)]
43 | [string]$BinariesDirectory = $Env:TF_BUILD_BINARIESDIRECTORY
44 | )
45 |
46 | Begin
47 | {
48 | if (-not (Test-Path $SourceDirectory)) {
49 | throw "The directory '$SourceDirectory' does not exist."
50 | }
51 |
52 | if (-not (Test-Path $BinariesDirectory)) {
53 | md $BinariesDirectory
54 | Write-Warning "The directory '$BinariesDirectory' did not exist and was created."
55 | }
56 |
57 | [int]$result = 0
58 | }
59 | Process
60 | {
61 | # Specify file types to include
62 | $FileTypes = $("*.app")
63 |
64 | # Specify the sub-folders to include
65 | $SourceSubFolders = $("*app.publish*")
66 |
67 | # Find the files
68 | $folders = Get-ChildItem $SourceDirectory -Recurse -Include $SourceSubFolders -Directory
69 | $files = $folders | foreach { Get-ChildItem -Path $_.FullName -Recurse -include $FileTypes }
70 |
71 |
72 |
73 | if($files)
74 | {
75 | Write-Verbose "Found $files.count files:"
76 |
77 | foreach ($file in $files) {
78 | Write-Verbose $file.FullName
79 | Copy-Item $file $BinariesDirectory
80 | $result++
81 | }
82 | }
83 | else
84 | {
85 | Write-Warning "Found no files."
86 | }
87 | }
88 | End
89 | {
90 | return $result
91 | }
92 | }
93 |
94 | if (-not $MyInvocation.Line.Contains("`$here\`$sut")){
95 | New-AppPackage -SourceDirectory $SourceDirectory -BinariesDirectory $BinariesDirectory
96 | }
--------------------------------------------------------------------------------
/Apps/Apps/Replace-Tokens.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param
3 | (
4 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=0)]
5 | [ValidateNotNullOrEmpty()]
6 | [string]$RootFolder,
7 |
8 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=1)]
9 | [ValidateNotNullOrEmpty()]
10 | [string]$FileName
11 | )
12 |
13 |
14 | function Replace-Tokens
15 | {
16 | [CmdletBinding()]
17 | Param
18 | (
19 | # Hilfebeschreibung zu Param1
20 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=0)]
21 | [ValidateNotNullOrEmpty()]
22 | [ValidateScript({ Test-Path $_ })]
23 | [string]$FileFullName
24 | )
25 |
26 | Write-Verbose "Replace tokens in '$FileFullName'..."
27 |
28 | # get the environment variables
29 | $vars = Get-ChildItem -Path env:*
30 |
31 | # read in the setParameters file
32 | $contents = Get-Content -Path $FileFullName
33 |
34 | # perform a regex replacement
35 | $newContents = "";
36 | $contents | ForEach-Object {
37 |
38 | $line = $_
39 | if ($_ -match "__(\w+)__") {
40 | $setting = $vars | Where-Object { $_.Name -eq $Matches[1] }
41 |
42 | if ($setting) {
43 | Write-Verbose "Replacing key '$($setting.Name)' with value '$($setting.Value)' from environment"
44 | $line = $_ -replace "__(\w+)__", $setting.Value
45 | }
46 | }
47 |
48 | $newContents += $line + [Environment]::NewLine
49 | }
50 |
51 | Write-Verbose -Verbose "Save content to '$FileFullName'."
52 | Set-Content $FileFullName -Value $newContents
53 |
54 | Write-Verbose "Done"
55 | }
56 |
57 | Write-Verbose "Look for file '$FileName' in '$RootFolder'..."
58 |
59 | $files = Get-ChildItem -Path $RootFolder -Recurse -Filter $FileName
60 |
61 | Write-Verbose "Found $($files.Count) files."
62 |
63 | $files | ForEach-Object { Replace-Tokens -FileFullName $_.FullName }
64 |
65 | Write-Verbose "All files processed."
66 |
67 |
--------------------------------------------------------------------------------
/Apps/Apps/Set-AssemblyVersion.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
3 | . "$here\$sut"
4 |
5 |
6 | Describe "Set-AssemblyVersion" {
7 |
8 | context "Given the SourceDirectory does not exit, it"{
9 |
10 | It "throws an exception"{
11 | { Set-AssemblyVersion -SourceDirectory "$TestDrive\XYZ" -BuildNumber "Dev_1.0.20150922.01" } | Should throw
12 | }
13 | }
14 |
15 | context "Given the SourceDirectory does exit, it"{
16 | setup -File "SourceDir\Properties\AssemblyInfo.cs" -Content "[assembly: AssemblyVersion(""1.0.0.0"")]"
17 |
18 | It "does not throw an exception"{
19 | { Set-AssemblyVersion -SourceDirectory "$TestDrive\SourceDir" -BuildNumber "Dev_1.0.20150922.01" } | Should not Throw
20 | }
21 |
22 | $content = Get-Content "$TestDrive\SourceDir\Properties\AssemblyInfo.cs"
23 |
24 | It "sets the version to the extracted verion of the build number"{
25 | $content | should match "[assembly: AssemblyVersion(""1.0.20150922.01"")]"
26 | }
27 | }
28 | }
29 |
30 | Describe "Get-Version"{
31 |
32 | context "Given a valid regex, it"{
33 |
34 | It "returns the expected version if one match was found"{
35 | $actual = Get-Version -BuildNumber "Build HelloWorld_0000.00.00.0" -VersionFormat "\d+\.\d+\.\d+\.\d+"
36 |
37 | $actual | Should be "0000.00.00.0"
38 | }
39 |
40 | It "returns the first result, if two results were found"{
41 | $actual = Get-Version -BuildNumber "1111.00.00.0_Build HelloWorld_0000.00.00.0" -VersionFormat "\d+\.\d+\.\d+\.\d+"
42 |
43 | $actual | Should be "1111.00.00.0"
44 | }
45 |
46 | It "throws an exception, if no match could be found"{
47 |
48 | { Get-Version -BuildNumber "Build HelloWorld_0" -VersionFormat "\d+\.\d+\.\d+\.\d+" } | Should throw
49 | }
50 | }
51 |
52 | context "Given an invalid regex, it"{
53 |
54 | It "throws an exception."{
55 |
56 | { Get-Version -BuildNumber "Build HelloWorld_0" -VersionFormat "lala" } | Should throw
57 | }
58 | }
59 | }
60 |
61 | Describe "Get-Files"{
62 |
63 | context "Given two projects in a solution folder, it"{
64 |
65 | setup -File "SourceDir\SolutionDir\Project1\Properties\AssemblyInfo.cs"
66 | setup -File "SourceDir\SolutionDir\Project2\Properties\AssemblyInfo.cs"
67 |
68 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
69 |
70 | It "returns two Assembly.cs files"{
71 | $actual | should not BeNullOrEmpty
72 | $actual.Count | should be 2
73 | }
74 | }
75 |
76 | context "Given two projects in different subfolders, it"{
77 |
78 | setup -File "SourceDir\SolutionDir\Subfolder1\Project1\Properties\AssemblyInfo.cs"
79 | setup -File "SourceDir\SolutionDir\Sub1\Sub2\Project2\Properties\AssemblyInfo.cs"
80 |
81 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
82 |
83 | It "returns two Assembly.cs files"{
84 | $actual | should not BeNullOrEmpty
85 | $actual.Count | should be 2
86 | }
87 | }
88 |
89 | context "Given a visual basic project, it"{
90 |
91 | setup -File "SourceDir\Properties\AssemblyInfo.vb"
92 |
93 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
94 |
95 | It "returns two Assembly.cs files"{
96 | $actual | should not BeNullOrEmpty
97 | $actual.Count | should be 1
98 | }
99 | }
100 | }
101 |
102 | Describe "Set-FileContent"{
103 |
104 | context "Given an AssemblyInfo.cs file, it"{
105 |
106 | setup -File "SourceDir\AssemblyInfo.cs" -Content "//comment`r`n[assembly: AssemblyVersion(""1.0.0.0"")]`r`n[assembly: AssemblyFileVersion(""1.0.0.0"")]`r`n"
107 |
108 | Set-FileContent (gci "$TestDrive\SourceDir") "15.88.99.17" "\d+\.\d+\.\d+\.\d+"
109 |
110 | $content = Get-Content "$TestDrive\SourceDir\AssemblyInfo.cs"
111 |
112 | It "sets the Version to the given version"{
113 | $content[1] | should match "[assembly: AssemblyVersion(""15.88.99.1"")]"
114 | }
115 |
116 | It "sets the FileVersion to the given version"{
117 | $content[2] | should match "[assembly: AssemblyFileVersion(""15.88.99.1"")]"
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/Apps/Apps/Set-AssemblyVersion.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Sets the assembly version of all assemblies in the source directory.
4 | .DESCRIPTION
5 | A build script that can be included in TFS 2015 or Visual Studio Online (VSO) vNevt builds that update the version of all assemblies in a workspace.
6 | It uses the name of the build to extract the version number and updates all AssemblyInfo.cs files to use the new version.
7 | .EXAMPLE
8 | Set-AssemblyVersion
9 | .EXAMPLE
10 | Set-AssemblyVersion -SourceDirectory $Env:BUILD_SOURCESDIRECTORY -BuildNumber $Env:BUILD_BUILDNUMBER
11 | .EXAMPLE
12 | Set-AssemblyVersion -SourceDirectory ".\SourceDir" -BuildNumber "Dev_1.0.20150922.01" -VersionFormat "\d+\.\d+\.\d+\.\d+"
13 | #>
14 |
15 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
16 | [Alias()]
17 | [OutputType([int])]
18 | Param
19 | (
20 | # The path to the source directory. Default $Env:BUILD_SOURCESDIRECTORY is set by TFS.
21 | [Parameter(Mandatory=$false, Position=0)]
22 | [ValidateNotNullOrEmpty()]
23 | [string]$SourceDirectory = $Env:BUILD_SOURCESDIRECTORY,
24 |
25 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
26 | [Parameter(Mandatory=$false, Position=1)]
27 | [ValidateNotNullOrEmpty()]
28 | [string]$BuildNumber = $Env:BUILD_BUILDNUMBER,
29 |
30 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
31 | [Parameter(Mandatory=$false, Position=2)]
32 | [ValidateNotNullOrEmpty()]
33 | [string]$VersionFormat = "\d+\.\d+\.\d+\.\d+"
34 | )
35 |
36 | <#
37 | .Synopsis
38 | Sets the assembly version of all assemblies in the source directory.
39 | .DESCRIPTION
40 | A build script that can be included in TFS 2015 or Visual Studio Online (VSO) vNevt builds that update the version of all assemblies in a workspace.
41 | It uses the name of the build to extract the version number and updates all AssemblyInfo.cs files to use the new version.
42 | .EXAMPLE
43 | Set-AssemblyVersion
44 | .EXAMPLE
45 | Set-AssemblyVersion -SourceDirectory $Env:BUILD_SOURCESDIRECTORY -BuildNumber $Env:BUILD_BUILDNUMBER
46 | .EXAMPLE
47 | Set-AssemblyVersion -SourceDirectory ".\SourceDir" -BuildNumber "Dev_1.0.20150922.01" -VersionFormat "\d+\.\d+\.\d+\.\d+"
48 | #>
49 | function Set-AssemblyVersion
50 | {
51 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
52 | [Alias()]
53 | [OutputType([int])]
54 | Param
55 | (
56 | # The path to the source directory. Default $Env:BUILD_SOURCESDIRECTORY is set by TFS.
57 | [Parameter(Mandatory=$false, Position=0)]
58 | [ValidateNotNullOrEmpty()]
59 | [string]$SourceDirectory = $Env:BUILD_SOURCESDIRECTORY,
60 |
61 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
62 | [Parameter(Mandatory=$false, Position=1)]
63 | [ValidateNotNullOrEmpty()]
64 | [string]$BuildNumber = $Env:BUILD_BUILDNUMBER,
65 |
66 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
67 | [Parameter(Mandatory=$false, Position=2)]
68 | [ValidateNotNullOrEmpty()]
69 | [string]$VersionFormat = "\d+\.\d+\.\d+\.\d+"
70 | )
71 |
72 | if (-not (Test-Path $SourceDirectory)) {
73 | throw "The directory '$SourceDirectory' does not exist."
74 | }
75 |
76 | $Version = Get-Version -BuildNumber $BuildNumber -VersionFormat $VersionFormat
77 |
78 | $files = Get-Files -SourceDirectory $SourceDirectory
79 |
80 | Set-FileContent -Files $files -Version $Version -VersionFormat $VersionFormat
81 | }
82 |
83 | function Get-Version
84 | {
85 | [CmdletBinding()]
86 | [Alias()]
87 | [OutputType([string])]
88 | Param
89 | (
90 | [Parameter(Mandatory=$true, Position=0)]
91 | [ValidateNotNullOrEmpty()]
92 | [string]$BuildNumber,
93 |
94 | [Parameter(Mandatory=$true, Position=1)]
95 | [ValidateNotNullOrEmpty()]
96 | [string]$VersionFormat
97 | )
98 |
99 | $VersionData = [regex]::matches($BuildNumber,$VersionFormat)
100 |
101 | if ($VersionData.Count -eq 0){
102 | throw "Could not find version number with format '$VersionFormat' in BUILD_BUILDNUMBER '$BuildNumber'."
103 | }
104 |
105 | return $VersionData[0]
106 | }
107 |
108 | function Get-Files
109 | {
110 | [CmdletBinding()]
111 | [Alias()]
112 | [OutputType([System.IO.FileSystemInfo[]])]
113 | Param
114 | (
115 | [Parameter(Mandatory=$true, Position=0)]
116 | [ValidateNotNullOrEmpty()]
117 | [string]$SourceDirectory
118 | )
119 |
120 | $folders = Get-ChildItem $SourceDirectory -Recurse -Include "*Properties*" | Where-Object { $_.PSIsContainer }
121 |
122 | return $folders | ForEach-Object { Get-ChildItem -Path $_.FullName -Recurse -include AssemblyInfo.* }
123 | }
124 |
125 | function Set-FileContent
126 | {
127 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
128 | [OutputType([int])]
129 | Param
130 | (
131 | [Parameter(Mandatory=$true, Position=0)]
132 | [System.IO.FileSystemInfo[]]$Files,
133 |
134 | [Parameter(Mandatory=$true, Position=1)]
135 | [string]$Version,
136 |
137 | [Parameter(Mandatory=$true, Position=2)]
138 | [string]$VersionFormat
139 | )
140 |
141 | foreach ($file in $Files)
142 | {
143 | $filecontent = Get-Content $file
144 |
145 | if ($PSCmdlet.ShouldProcess("$file", "Set-AssemblyVersion"))
146 | {
147 | attrib $file -r
148 | $filecontent -replace $VersionFormat, $Version | Out-File $file
149 | Write-Verbose -Message "Applied Version '$Version' $($file.FullName) - version applied"
150 | }
151 | }
152 | }
153 |
154 | if ($myinvocation.line.Contains("`$here\`$sut")) {
155 | Write-Verbose -Message "Script was called ba pester: $($myinvocation.line)"
156 |
157 | }else{
158 |
159 | Write-Verbose -Message "Script was called with the following arguments:"
160 | Write-Verbose -Message "SourceDirectory: $SourceDirectory"
161 | Write-Verbose -Message "BuildNumber: $BuildNumber"
162 | Write-Verbose -Message "VersionFormat: $VersionFormat"
163 | Set-AssemblyVersion -SourceDirectory $SourceDirectory -BuildNumber $BuildNumber -VersionFormat $VersionFormat
164 | }
--------------------------------------------------------------------------------
/Apps/Apps/Trust-SPApp.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
2 | [OutputType([int])]
3 | Param
4 | (
5 | [Parameter(Mandatory=$true, Position=0)]
6 | [ValidateNotNullOrEmpty()]
7 | [string]$AppName,
8 |
9 | [Parameter(Mandatory=$true, Position=1)]
10 | [ValidateNotNullOrEmpty()]
11 | [string]$WebUrl,
12 |
13 | [Parameter(Mandatory=$true, Position=2)]
14 | [ValidateNotNullOrEmpty()]
15 | [string]$RemoteAppUrl,
16 |
17 | [Parameter(Mandatory=$true, Position=3)]
18 | [ValidateNotNullOrEmpty()]
19 | [string]$DeployUserName,
20 |
21 | [Parameter(Mandatory=$true, Position=4)]
22 | [ValidateNotNullOrEmpty()]
23 | [string]$DeployPassword
24 | )
25 |
26 | function Load-Assemblies
27 | {
28 | # suppress output
29 | $assembly1 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
30 | $assembly2 = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
31 |
32 | Write-Verbose "Assemblies loaded."
33 | }
34 |
35 | function WaitFor-IEReady
36 | {
37 | [CmdletBinding()]
38 | Param
39 | (
40 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
41 | $ie,
42 |
43 | [Parameter(Mandatory=$false, Position=1)]
44 | $initialWaitInSeconds = 1
45 | )
46 |
47 | sleep -Seconds $initialWaitInSeconds
48 |
49 | while ($ie.Busy) {
50 |
51 | sleep -milliseconds 50
52 | }
53 | }
54 |
55 | <#
56 | .Synopsis
57 | Invoke a java script function.
58 | .DESCRIPTION
59 | Use this function to run JavaScript on a web page. Your $Command can
60 | return a value which will be returned by this function unless $global
61 | switch is specified in which case $Command will be executed in global
62 | scope and cannot return a value. If you received error 80020101 it means
63 | you need to fix your JavaScript code.
64 | .EXAMPLE
65 | Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);'
66 | .EXAMPLE
67 | $result = Invoke-JavaScript -IE $ie -Command 'Post.IsSubmitReady();setTimeout(function() {Post.SubmitCreds(); }, 1000);' -Global
68 | #>
69 | function Invoke-JavaScript
70 | {
71 | [CmdletBinding()]
72 | [OutputType([string])]
73 | Param
74 | (
75 | [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)]
76 | $IE,
77 |
78 | [Parameter(Mandatory=$true, Position=1)]
79 | $Command,
80 |
81 | [switch]$Global
82 | )
83 |
84 | if (-not $Global.IsPresent) {
85 | $Command = "document.body.setAttribute('PSResult', (function(){ $Command })());"
86 | }
87 |
88 | $document = $IE.document
89 | $window = $document.parentWindow
90 | $window.execScript($Command, 'javascript') | Out-Null
91 |
92 | if (-not $Global.IsPresent) {
93 | return $document.body.getAttribute('PSResult')
94 | }
95 | }
96 |
97 | function Trust-SPAddIn
98 | {
99 | [CmdletBinding(SupportsShouldProcess=$true)]
100 | [OutputType([int])]
101 | Param
102 | (
103 | [Parameter(Mandatory=$true, Position=0)]
104 | [guid]$AppInstanceId,
105 |
106 | [Parameter(Mandatory=$true, Position=1)]
107 | [string]$WebUrl,
108 |
109 | [parameter(Mandatory=$true, Position=2)]
110 | [string]$UserName,
111 |
112 | [parameter(Mandatory=$true, Position=3)]
113 | [string]$Password
114 | )
115 |
116 | $authorizeURL = "$($WebUrl.TrimEnd('/'))/_layouts/15/appinv.aspx?AppInstanceId={$AppInstanceId}"
117 |
118 | $ie = New-Object -com internetexplorer.application
119 |
120 | try
121 | {
122 | $ie.Visible = $false
123 | $ie.Navigate2($authorizeURL)
124 |
125 | WaitFor-IEReady $ie
126 |
127 | if ($ie.LocationName -eq "Sign in to Office 365"){
128 |
129 | Write-Verbose "Authenticate $UserName to O365..."
130 | # Authorize against O365
131 | $useAnotherLink = $ie.Document.getElementById("use_another_account_link")
132 | if ($useAnotherLink) {
133 |
134 | WaitFor-IEReady $ie
135 |
136 | $useAnotherLink.Click()
137 |
138 | WaitFor-IEReady $ie
139 |
140 | }
141 |
142 | $credUseridInputtext = $ie.Document.getElementById("cred_userid_inputtext")
143 | $credUseridInputtext.value = $UserName
144 |
145 | $credPasswordInputtext = $ie.Document.getElementById("cred_password_inputtext")
146 | $credPasswordInputtext.value = $Password
147 |
148 | WaitFor-IEReady $ie
149 |
150 |
151 | # make a jQuery call
152 | $result = Invoke-JavaScript -IE $ie -Command "`nPost.IsSubmitReady();`nsetTimeout(function() {`nPost.SubmitCreds();`n}, 1000);"
153 |
154 | WaitFor-IEReady $ie -initialWaitInSeconds 5
155 |
156 | }
157 |
158 | Write-Verbose $ie.Document.Title
159 |
160 |
161 | if ($ie.LocationName -eq "Do you trust CalculatorApp-Dev?")
162 | {
163 | sleep -seconds 5
164 |
165 | $button = $ie.Document.getElementById("ctl00_PlaceHolderMain_BtnAllow")
166 | if ($button -eq $null) {
167 |
168 | throw "Could not find button to press"
169 |
170 | }else{
171 |
172 | $button.click()
173 |
174 | WaitFor-IEReady $ie
175 |
176 | #if the button press was successful, we should now be on the Site Settings page..
177 | if ($ie.Document.title -like "*trust*") {
178 |
179 | throw "Error: $($ie.Document.body.getElementsByClassName("ms-error").item().InnerText)"
180 |
181 | }else{
182 |
183 | Write-Verbose "App was trusted successfully!"
184 | }
185 | }
186 |
187 | }else{
188 |
189 | throw "Unexpected page '$($ie.LocationName)' was loaded. Please check your url."
190 | }
191 | }
192 | finally
193 | {
194 | $ie.Quit()
195 | }
196 | }
197 |
198 |
199 | function Get-AppPackageInformations{
200 |
201 | [CmdletBinding()]
202 | [OutputType([Hashtable])]
203 | param([string]$Path, [string]$AppWebUrl)
204 |
205 | # Open zip
206 | Add-Type -assembly System.IO.Compression.FileSystem
207 | Write-Verbose "Open zip file '$Path'..."
208 | $zip = [System.IO.Compression.ZipFile]::Open($Path, "Update")
209 |
210 | try{
211 | $fileToEdit = "AppManifest.xml"
212 | $file = $zip.Entries.Where({$_.name -eq $fileToEdit})
213 |
214 | Write-Verbose "Read app manifest from '$file'."
215 | $desiredFile = [System.IO.StreamReader]($file).Open()
216 | [xml]$xml = $desiredFile.ReadToEnd()
217 | $desiredFile.Close()
218 |
219 | $appInformations = @{
220 | ClientId = $xml.App.AppPrincipal.RemoteWebApplication.ClientId
221 | Version = $xml.App.Version
222 | AllowAppOnlyPolicy = [bool]$xml.App.AppPermissionRequests.AllowAppOnlyPolicy
223 | Title = $xml.App.Properties.Title
224 | ProductID = $xml.App.ProductID
225 | }
226 |
227 | if ($AppWebUrl){
228 | # Replace URL
229 | $value = $xml.App.Properties.StartPage
230 | Write-Verbose "Replace URL in '$value' with '$AppWebUrl'."
231 | $value = $value -replace "^.*\?","$($AppWebUrl)?"
232 | $xml.App.Properties.StartPage = $value
233 |
234 |
235 | # Save file
236 | Write-Verbose "Save manifest to '$file'."
237 | $desiredFile = [System.IO.Stream]($file).Open()
238 | $desiredFile.SetLength(0)
239 | $xml.Save($desiredFile)
240 | $desiredFile.Flush()
241 | $desiredFile.Close()
242 | $desiredFile.Dispose()
243 |
244 | $desiredFile = [System.IO.StreamReader]($file).Open()
245 | [xml]$xml = $desiredFile.ReadToEnd()
246 | Write-Host $xml
247 | $desiredFile.Close()
248 | $desiredFile.Dispose()
249 | }
250 |
251 | return $appInformations
252 |
253 | }finally{
254 | # Write the changes and close the zip file
255 | $zip.Dispose()
256 | }
257 | }
258 |
259 | Write-Host "Look for '$AppName' in '$applicationPath'..."
260 | $appPackage = Get-ChildItem -Path $applicationPath -Recurse -Include $AppName
261 |
262 | if (-not($appPackage)){ throw "No ap ppackage '$AppName' found in '$applicationPath'..." }
263 |
264 | Write-Host "Get package informations and set url in package to '$RemoteAppUrl'"
265 | $appPackageInformations = Get-AppPackageInformations -Path $appPackage.FullName -AppWebUrl $RemoteAppUrl
266 |
267 |
268 | Load-Assemblies
269 |
270 | Write-Host "Connect to '$WebUrl' as '$DeployUserName'..."
271 | $clientContext = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)
272 | $clientContext.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($DeployUserName, (ConvertTo-SecureString $DeployPassword -AsPlainText -Force))
273 |
274 | try
275 | {
276 | $web = $clientContext.Web
277 | $clientContext.Load($web)
278 |
279 | $clientContext.ExecuteQuery();
280 |
281 | Write-Host "Successfully connected to '$WebUrl'..."
282 |
283 | Write-Host "Update SharePoint app '$AppName' in '$WebUrl' to Version '$($appPackageInformations.Version)'..."
284 | $appId = Install-App -clientContext $clientContext -appPackage $appPackage.FullName -productId $appPackageInformations.ProductID
285 | Write-Host "Done."
286 |
287 | Write-Host "Trust App..."
288 | Trust-SPAddIn -AppInstanceId $appId -WebUrl $WebUrl -UserName $DeployUserName -Password $DeployPassword
289 | Write-Host "Done."
290 | }
291 | finally
292 | {
293 | $clientContext.Dispose()
294 | }
--------------------------------------------------------------------------------
/DSCPublishing/DSCResourcesConfiguration.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | # Ensure the current state of the DSC resource modules on the current server.
3 | # This file is optimized for release management.
4 | # Use Ensure-DSCResources.ps1 to manually deploy the resources.
5 | #>
6 | Configuration DSCResourceConfiguration
7 | {
8 | Node $env:COMPUTERNAME
9 | {
10 | File DSCModules
11 | {
12 | SourcePath = "$applicationPath\DSCResources"
13 | DestinationPath = "$env:ProgramFiles\WindowsPowerShell\Modules"
14 | Type = "Directory"
15 | Recurse = $true
16 | Ensure = "Present"
17 | }
18 | }
19 | }
20 |
21 | DSCResourceConfiguration
--------------------------------------------------------------------------------
/DSCPublishing/Ensure-DSCResources.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | # Script to run the "DSCResourcesConfiguration.ps1" and start the DSC-Configuration.
3 | # Note: run this script in the root of the git repository (i.e. \Source\Repos\SPAdministration\DSCResources).
4 | #>
5 | $applicationPath = Resolve-Path .
6 |
7 | . .\DSCResourcesConfiguration.ps1
8 |
9 | Start-DscConfiguration DSCResourceConfiguration -Wait -Force -Verbose
--------------------------------------------------------------------------------
/DSCPublishing/Package.ps1:
--------------------------------------------------------------------------------
1 | # Specify file types to exclude
2 | $FileTypes = $("*.log","*.proj","*.vsprops")
3 |
4 | $SourceDir = "$($Env:TF_BUILD_SOURCESDIRECTORY.TrimEnd('\'))\DSCResources"
5 |
6 | # Find the files
7 | $files = gci $SourceDir -Recurse -Exclude $FileTypes
8 |
9 | if($files)
10 | {
11 | Write-Verbose "Found $files.count files:"
12 |
13 | foreach ($file in $files) {
14 | Write-Verbose $file.FullName
15 | }
16 | }
17 | else
18 | {
19 | Write-Warning "Found no files."
20 | }
21 |
22 | # If binary output directory exists, make sure it is empty
23 | # If it does not exist, create one
24 | # (this happens when 'Clean workspace' build process parameter is set to True)
25 | if ([IO.Directory]::Exists($Env:TF_BUILD_BINARIESDIRECTORY))
26 | {
27 | $DeletePath = $Env:TF_BUILD_BINARIESDIRECTORY + "\*"
28 | Write-Verbose "$Env:TF_BUILD_BINARIESDIRECTORY exists."
29 | if(-not $Disable)
30 | {
31 | Write-Verbose "Ready to delete $DeletePath"
32 | Remove-Item $DeletePath -recurse
33 | Write-Verbose "Files deleted."
34 | }
35 | }
36 | else
37 | {
38 | Write-Verbose "$Env:TF_BUILD_BINARIESDIRECTORY does not exist."
39 |
40 | if(-not $Disable)
41 | {
42 | Write-Verbose "Ready to create it."
43 | [IO.Directory]::CreateDirectory($Env:TF_BUILD_BINARIESDIRECTORY) | Out-Null
44 | Write-Verbose "Directory created."
45 | }
46 | }
47 |
48 | # Copy the binaries
49 | Write-Verbose "Ready to copy files."
50 | if(-not $Disable)
51 | {
52 | Copy $SourceDir $Env:TF_BUILD_BINARIESDIRECTORY -Recurse -Exclude $FileTypes
53 | Write-Verbose "Files copied."
54 | }
55 |
--------------------------------------------------------------------------------
/DSCPublishing/README.md:
--------------------------------------------------------------------------------
1 | # DSC Publishing
2 | ## TFSBuild.proj and Package.ps1
3 | TFSBuild.proj is a simple project file that can be used to package files in a TFS Team build.
4 | The project file calls package.ps1 to copy all files to the drop folder.
5 |
6 | U can use this to package your dsc resources from a git repository using its own build.
7 |
8 | ## DSCResourcesConfiguration.ps1 and Ensure-DSCResources.ps1
9 | Use DSCResourcesConfiguration.ps1 to ensure the dsc resources on all machines before deploying any configurations in Releasemanagement. Use Ensure-DSCResources.ps1 to locally deploy the resources.
--------------------------------------------------------------------------------
/DSCPublishing/TFSBuild.proj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/DevOps/DevOps.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "DevOps", "DevOps\DevOps.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 |
--------------------------------------------------------------------------------
/DevOps/DevOps/Backup-Database.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Backup Database
4 | .DESCRIPTION
5 | make a full backup of a given database
6 | .EXAMPLE
7 | Backup-Database LSK -ServerInstance lissval-t02
8 | .EXAMPLE
9 | #>
10 | [CmdletBinding()]
11 | Param
12 | (
13 | [Parameter(Mandatory=$true,
14 | ValueFromPipelineByPropertyName=$true,
15 | Position=0)]
16 | [string]
17 | $DataBaseName,
18 |
19 | [Parameter(Mandatory=$true,
20 | ValueFromPipelineByPropertyName=$true,
21 | Position=1)]
22 | [string]
23 | $ServerInstance
24 | )
25 |
26 | Begin
27 | {
28 | }
29 | Process
30 | {
31 | Backup-SqlDatabase $DataBaseName -ServerInstance $ServerInstance
32 | }
33 | End
34 | {
35 | }
--------------------------------------------------------------------------------
/DevOps/DevOps/Backup-Website.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Backup an existing Database
4 | .DESCRIPTION
5 | Moves or copies all the content of a website to a backup folder.
6 | .EXAMPLE
7 | Backup-Website -WebsiteName LSK.Portal
8 | #>
9 | [CmdletBinding()]
10 | Param
11 | (
12 | [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)]
13 | [string]
14 | $WebSiteName,
15 |
16 | [string]
17 | $WebSitePath = "C:\inetpub\wwwroot\",
18 |
19 | [string]
20 | $BackupPath = "C:\Deployment\Backup\",
21 |
22 | [switch]
23 | $Clean
24 | )
25 |
26 | Begin
27 | {
28 | $BackupFolder = Join-Path $BackupPath $WebSiteName
29 | $WebSiteFullName = Join-Path $WebSitePath $WebSiteName
30 |
31 | if (-not (Test-Path $WebSiteFullName))
32 | {
33 | $message = "Website '$WebSiteFullName' does not exist."
34 | $exception = New-Object InvalidOperationException $message
35 | $errorID = 'FolderNotFound'
36 | $errorCategory = [Management.Automation.ErrorCategory]::InvalidArgument
37 | $target = $WebSiteName
38 | $errorRecord = New-Object Management.Automation.ErrorRecord $exception, $errorID, $errorCategory, $target
39 | $PSCmdlet.ThrowTerminatingError($errorRecord)
40 | }
41 |
42 | if (Test-Path $BackupFolder)
43 | {
44 | Write-Verbose "Cleaning up old backup in '$BackupFolder'..."
45 | Remove-Item $BackupFolder -Force -Recurse
46 | Write-Verbose "Done."
47 | }
48 |
49 | Write-Verbose "Create backup folder '$BackupFolder'..."
50 | md $BackupFolder | Out-Null
51 | Write-Verbose "Done."
52 | }
53 |
54 | Process
55 | {
56 | Write-Verbose "Backup site '$WebSiteFullName' to backup location '$BackupFolder'..."
57 | Get-ChildItem -Path $WebSiteFullName | % {
58 | if ($Clean.IsPresent)
59 | {
60 | Write-Verbose "Move '$_.FullName'..."
61 | Move-Item $_.FullName "$BackupFolder" -Force
62 | }
63 | else
64 | {
65 | Write-Verbose "Copy '$_.FullName'..."
66 | Copy-Item $_.FullName "$BackupFolder" -Force -Recurse
67 | }
68 |
69 | Write-Verbose "Done."
70 | }
71 | }
72 |
73 | End
74 | {
75 | }
--------------------------------------------------------------------------------
/DevOps/DevOps/DevOps.pssproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | 6CAFC0C6-A428-4d30-A9F9-700E829FEA51
7 | Exe
8 | MyApplication
9 | MyApplication
10 | DevOps
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug\
17 | DEBUG;TRACE
18 | prompt
19 | 4
20 |
21 |
22 | pdbonly
23 | true
24 | bin\Release\
25 | TRACE
26 | prompt
27 | 4
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/DevOps/DevOps/Get-TFSRelease.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param (
3 | [Parameter(Mandatory=$true, Position=0)]
4 | $ProjectCollection
5 | )
6 |
7 | $projectsResult = Invoke-RestMethod "$ProjectCollection/_apis/projects?api-version=1.0" -Method Get -UseDefaultCredentials
8 |
9 | $releases = @()
10 |
11 | $projectsResult.value | % {
12 |
13 | $project = $_.name
14 |
15 | $url = "$ProjectCollection/$project/_apis/release/releases?api-version=2.2-preview.1"
16 |
17 | $result = Invoke-RestMethod $url -Method Get -UseDefaultCredentials
18 |
19 | if ($result.count -gt 0){
20 |
21 | $result.value | % {
22 |
23 | $releases += [pscustomobject]@{
24 | Project = $project
25 | Name = $_.Name
26 | Status = $_.status
27 | CreatedOn = [datetime]$_.createdOn
28 | ModifiedOn = [datetime]$_.modifiedOn
29 | }
30 | }
31 | }
32 | }
33 |
34 | $releases | Sort-Object -Property ModifiedOn -Descending | Format-Table -AutoSize
--------------------------------------------------------------------------------
/DevOps/DevOps/Get-VSTSRelease.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param (
3 | [Parameter(Mandatory=$true, Position=0)]
4 | $Account,
5 |
6 | [Parameter(Mandatory=$true, Position=1)]
7 | $Accesstoken
8 | )
9 |
10 | $passkey = ":$($Accesstoken)"
11 | $encodedKey = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passkey))
12 | $token = "Basic $encodedKey"
13 |
14 | $projectsResult = Invoke-RestMethod "https://$Account.visualstudio.com/_apis/projects?api-version=1.0" -Method Get -Headers @{ Authorization = $token }
15 |
16 | $releases = @()
17 |
18 | $projectsResult.value | % {
19 |
20 | $project = $_.name
21 |
22 | $url = "https://$Account.vsrm.visualstudio.com/$project/_apis/release/releases?api-version=3.0-preview.2"
23 |
24 | $result = Invoke-RestMethod $url -Method Get -Headers @{ Authorization = $token }
25 |
26 | if ($result.count -gt 0){
27 |
28 | $result.value | % {
29 |
30 | $releases += [pscustomobject]@{
31 | Project = $project
32 | Name = $_.Name
33 | Status = $_.status
34 | CreatedOn = [datetime]$_.createdOn
35 | ModifiedOn = [datetime]$_.modifiedOn
36 | }
37 | }
38 | }
39 | }
40 |
41 | $releases | Sort-Object -Property ModifiedOn -Descending | Format-Table -AutoSize
--------------------------------------------------------------------------------
/DevOps/DevOps/Restore-Website.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Restores a website.
4 | .DESCRIPTION
5 | Restores a website to its original location.
6 | .EXAMPLE
7 | Restore-Website -WebsiteName LSK.Portal
8 | #>
9 | [CmdletBinding()]
10 | Param
11 | (
12 | [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)]
13 | [string]
14 | $WebSiteName,
15 |
16 | [string]
17 | $WebSitePath = "C:\inetpub\wwwroot\",
18 |
19 | [string]
20 | $BackupPath = "C:\Deployment\Backup\"
21 | )
22 |
23 | Begin
24 | {
25 | $BackupFolder = Join-Path $BackupPath $WebSiteName
26 | $WebSiteFullName = Join-Path $WebSitePath $WebSiteName
27 |
28 | if (-not (Test-Path $BackupFolder))
29 | {
30 | $message = "Backup '$BackupFolder' does not exist."
31 | $exception = New-Object InvalidOperationException $message
32 | $errorID = 'FolderNotFound'
33 | $errorCategory = [Management.Automation.ErrorCategory]::InvalidArgument
34 | $target = $BackupFolder
35 | $errorRecord = New-Object Management.Automation.ErrorRecord $exception, $errorID, $errorCategory, $target
36 | $PSCmdlet.ThrowTerminatingError($errorRecord)
37 | }
38 |
39 | if (-not (Test-Path $WebSiteFullName))
40 | {
41 | Write-Verbose "Create folder '$WebSiteFullName'..."
42 | md $WebSiteFullName | Out-Null
43 | Write-Verbose "Done."
44 | }
45 | }
46 |
47 | Process
48 | {
49 | Write-Verbose "Moving web site '$WebsiteName' from backup location '$BackupFolder' to '$WebSiteFullName'..."
50 | Get-ChildItem -Path $BackupFolder | % {
51 | Write-Verbose "Copy '$_.FullName'..."
52 | Copy-Item $_.FullName "$WebSiteFullName" -Force -Recurse
53 |
54 | Write-Verbose "Done."
55 | }
56 | }
57 |
58 | End
59 | {
60 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ScriptRepository
2 | A repo for storing scripts I might want to reuse...
3 |
4 | ## Description
5 | Look at each folder for its own readme. The scripts should more or less all deal with DSC, TFS, ReleaseManagemnt.
6 | If not they are about SharePoint or O365 and will later be transformed into the xSharePointAdministration or xSharePointProvisioning resources.
7 |
--------------------------------------------------------------------------------
/Set-RepositoryPermission/Readme.md:
--------------------------------------------------------------------------------
1 | # NAME
2 | Set-RepositoryPermission.ps1
3 |
4 | # SYNOPSIS
5 | Set the permissions for a git repository to enforce naming policies
6 |
7 | # SYNTAX
8 | ``` PowerShell
9 | .\Set-RepositoryPermission.ps1 [-AccountOrCollection] [-ProjectName] [-Repository] [[-AllowedFolders] ] [[-AdminAllowedFolders] ] [[-PathToTfExe] ] [-WhatIf] [-Confirm] []
10 | ```
11 |
12 | # DESCRIPTION
13 | This script sets the permission of a git repository. It denys to create branches in the >>root and only allows the creation of branches in specific folders.
14 |
15 | # PARAMETERS
16 | -AccountOrCollection
17 | Your VSTS account or TFS project collection url
18 |
19 | -ProjectName
20 | The name of the project that contains your repository
21 |
22 | -Repository
23 | The name of the repository
24 |
25 | -AllowedFolders
26 | The list of folders where contributors can create branches in (default: features, bufixes, users)
27 |
28 | -AdminAllowedFolders
29 | The list of folders where contributors can create branches in (default: features, bufixes, users, releases, master)
30 |
31 | -PathToTfExe
32 | The path where tf.exe is installed (default: Visual Studio 2017 location 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer').
33 |
34 | -WhatIf []
35 |
36 | -Confirm []
37 |
38 |
39 | This cmdlet supports the common parameters: Verbose, Debug,
40 | ErrorAction, ErrorVariable, WarningAction, WarningVariable,
41 | OutBuffer, PipelineVariable, and OutVariable. For more information, see
42 | about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).
43 |
44 | # Examples
45 |
46 | ## -------------------------- EXAMPLE 1 --------------------------
47 | ``` PowerShell
48 | C:\PS>Set-RepositoryPermission.ps1 -AccountOrCollection https://tfs.contoso.com/tfs/DefaultCollection -ProjectName MyProject -Repository MyProject -Verbose
49 | ```
50 |
51 | ## -------------------------- EXAMPLE 2 --------------------------
52 | ``` PowerShell
53 | C:\PS>Set-RepositoryPermission.ps1 -AccountOrCollection https://constoso.visualstudio.com -ProjectName MyProject -Repository MyProject -Verbose
54 | ```
55 |
56 | ## -------------------------- EXAMPLE 3 --------------------------
57 | ``` PowerShell
58 | C:\PS>Set-RepositoryPermission.ps1 -AccountOrCollection https://constoso.visualstudio.com -ProjectName MyProject -Repository MyProject -AllowedFolders @("features", "users")
59 | ```
60 |
61 |
62 | # REMARKS
63 | To see the examples, type: "get-help .\Set-RepositoryPermission.ps1 -examples".
64 | For more information, type: "get-help .\Set-RepositoryPermission.ps1 -detailed".
65 | For technical information, type: "get-help .\Set-RepositoryPermission.ps1 -full".
66 |
--------------------------------------------------------------------------------
/Set-RepositoryPermission/Set-RepositoryPermission.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Set the permissions for a git repository to enforce naming policies
4 | .DESCRIPTION
5 | This script sets the permission of a git repository. It denys to create branches in the root and only allows the creation of branches in specific folders.
6 | .EXAMPLE
7 | Set-RepositoryPermission.ps1 -AccountOrCollection https://tfs.contoso.com/tfs/DefaultCollection -ProjectName MyProject -Repository MyProject -Verbose
8 | .EXAMPLE
9 | Set-RepositoryPermission.ps1 -AccountOrCollection https://constoso.visualstudio.com -ProjectName MyProject -Repository MyProject -Verbose
10 | .EXAMPLE
11 | Set-RepositoryPermission.ps1 -AccountOrCollection https://constoso.visualstudio.com -ProjectName MyProject -Repository MyProject -AllowedFolders @("features", "users")
12 | #>
13 | [CmdletBinding(SupportsShouldProcess=$true)]
14 | Param
15 | (
16 | # Your VSTS account or TFS project collection url
17 | [Parameter(Mandatory=$true)]
18 | [ValidateNotNullOrEmpty()]
19 | [ValidatePattern("http://.*|https://.*")]
20 | [string]
21 | $AccountOrCollection,
22 |
23 | # The name of the project that contains your repository
24 | [Parameter(Mandatory=$true)]
25 | [ValidateNotNullOrEmpty()]
26 | [string]
27 | $ProjectName,
28 |
29 | # The name of the repository
30 | [Parameter(Mandatory=$true)]
31 | [ValidateNotNullOrEmpty()]
32 | [string]
33 | $Repository,
34 |
35 | # The list of folders where contributors can create branches in (default: features, bufixes, users)
36 | [Parameter(Mandatory=$false)]
37 | [string[]]
38 | $AllowedFolders = @("features", "bugfixes", "users"),
39 |
40 | # The list of folders where contributors can create branches in (default: features, bufixes, users, releases, master)
41 | [Parameter(Mandatory=$false)]
42 | [string[]]
43 | $AdminAllowedFolders = @("features", "bugfixes", "users", "releases", "master"),
44 |
45 | # The path where tf.exe is installed (default: Visual Studio 2017 location 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer').
46 | [Parameter(Mandatory=$false)]
47 | [ValidateNotNullOrEmpty()]
48 | [ValidateScript({ Test-Path -PathType Container $_ })]
49 | [string]
50 | $PathToTfExe = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
51 | )
52 |
53 | pushd $PathToTfExe
54 |
55 |
56 | # block the Create Branch permission at the repository root for the project's contributors
57 | Write-Verbose "Deny the 'CreateBranch' permission to the group '[$ProjectName]\Contributors' in repository '$Repository' ($AccountOrCollection)."
58 | if ($pscmdlet.ShouldProcess("$Repository", "Deny CreateBranch"))
59 | {
60 | & .\tf.exe git permission /deny:CreateBranch /group:[$ProjectName]\Contributors /collection:$AccountOrCollection /teamproject:$ProjectName /repository:$Repository
61 | }
62 |
63 | # block the Create Branch permission at the repository root for the project's admins
64 | Write-Verbose "Deny the 'CreateBranch' permission to the group '[$ProjectName]\Project Administrators' in repository '$Repository' ($AccountOrCollection)."
65 | if ($pscmdlet.ShouldProcess("$Repository", "Deny CreateBranch"))
66 | {
67 | & .\tf.exe git permission /deny:CreateBranch /group:"[$ProjectName]\Project Administrators" /collection:$AccountOrCollection /teamproject:$ProjectName /repository:$Repository
68 | }
69 |
70 | # Allow contributors to create branches under configured folders
71 | $AllowedFolders | ForEach-Object {
72 |
73 | Write-Verbose "Allow the 'CreateBranch' permission to the group '[$ProjectName]\Contributors' for folder '$_' in repository '$Repository' ($AccountOrCollection)."
74 | if ($pscmdlet.ShouldProcess("$Repository/$_", "Allow CreateBranch"))
75 | {
76 | & .\tf.exe git permission /allow:CreateBranch /group:[$ProjectName]\Contributors /collection:$AccountOrCollection /teamproject:$ProjectName /repository:$Repository /branch:$_
77 | }
78 | }
79 |
80 | # Allow admins to create branches under configured folders
81 | $AdminAllowedFolders | ForEach-Object {
82 |
83 | Write-Verbose "Allow the 'CreateBranch' permission to the group '[$ProjectName]\Project Administrators' for folder '$_' in repository '$Repository' ($AccountOrCollection)."
84 | if ($pscmdlet.ShouldProcess("$Repository/$_", "Allow CreateBranch"))
85 | {
86 | & .\tf.exe git permission /allow:CreateBranch /group:"[$ProjectName]\Project Administrators" /collection:$AccountOrCollection /teamproject:$ProjectName /repository:$Repository /branch:$_
87 | }
88 | }
89 |
90 | popd
91 |
92 |
93 |
--------------------------------------------------------------------------------
/TFS/ReleaseVariables/Get-ReleasesWithVariable.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Lists all releases with all environments that contain a specific variable.
4 | .DESCRIPTION
5 | Iterates one or more ProjectCollections and lists all environments in all release definitions in all projects. If the environment contains a CertificateThumbprint it will be displayed as well.
6 | .EXAMPLE
7 | Get-ReleasesWithVariable https:///tfs/ MyVariable
8 | .EXAMPLE
9 | Get-ReleasesWithVariable -ProjectCollections @("https://m/tfs/", "https://m/tfs/") -VariableName MyVariable -verbose
10 | .EXAMPLE
11 | Get-ReleasesWithVariable -AccountName VSTSAccountName -AccessToken PAT -VariableName MyVariable
12 | #>
13 | [CmdletBinding(DefaultParameterSetName='TFS')]
14 | Param
15 | (
16 | # One or more URLs to project collections on your on premise TFS.
17 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='TFS')]
18 | [string[]]
19 | $ProjectCollections,
20 |
21 | # The name of the VSTS account
22 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='VSTS')]
23 | [string]
24 | $AccountName,
25 |
26 | # The private access token (PAT). See https://www.visualstudio.com/en-us/docs/setup-admin/team-services/use-personal-access-tokens-to-authenticate
27 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='VSTS')]
28 | $Accesstoken,
29 |
30 | # The name of the variable to search for.
31 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='TFS')]
32 | [Parameter(Mandatory=$true, Position=2, ParameterSetName='VSTS')]
33 | [ValidateNotNullOrEmpty()]
34 | [string]
35 | $VariableName
36 | )
37 |
38 |
39 | if ($PSCmdlet.ParameterSetName -eq "TFS")
40 | {
41 | # TFS On Premise
42 | $apiVersion = "2.2-preview.1"
43 | $additionalParameters = @{ UseDefaultCredentials = $null }
44 | }
45 | else
46 | {
47 | # VSTS
48 | $passkey = ":$($Accesstoken)"
49 | $encodedKey = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passkey))
50 | $token = "Basic $encodedKey"
51 | $apiVersion = "3.0-preview.2"
52 |
53 | $ProjectCollections = "https://$AccountName.visualstudio.com"
54 |
55 | $additionalParameters = @{ Headers = @{ Authorization = $token } }
56 | }
57 |
58 | $releases = @()
59 |
60 | $ProjectCollections | % {
61 |
62 | $projectCollection = $_
63 | Write-Verbose "Processing ProjectCollection '$projectCollection'..."
64 |
65 | $projectsResult = Invoke-RestMethod "$projectCollection/_apis/projects?`$top=1000&api-version=1.0" -Method Get @additionalParameters
66 |
67 | if ($PSCmdlet.ParameterSetName -eq "VSTS")
68 | {
69 | $projectCollection = "https://$AccountName.vsrm.visualstudio.com"
70 | }
71 |
72 | $projectsResult.value | % {
73 |
74 | $project = $_.name
75 |
76 | Write-Host "Processing ProjectCollection '$project'..."
77 |
78 | $url = "$ProjectCollection/$project/_apis/release/definitions?`$expand=environments&api-version=$ApiVersion"
79 |
80 | $result = Invoke-RestMethod $url -Method Get @additionalParameters
81 |
82 | if ($result.count -gt 0){
83 |
84 | $result.value | % {
85 |
86 | $url = "$ProjectCollection/$project/_apis/release/definitions/$($_.id)?api-version=$ApiVersion"
87 |
88 | $definition = Invoke-RestMethod $url -Method Get @additionalParameters
89 |
90 | $name = $_.Name
91 |
92 | $definition.environments | % {
93 |
94 | if ($null -eq $_.variables.$VariableName)
95 | {
96 | Write-Verbose "Environment '$($_.Name)' does not contain a variable '$VariableName'."
97 | }
98 | else
99 | {
100 | $releases += [pscustomobject]@{
101 | Collection = $ProjectCollection
102 | Project = $project
103 | Name = $name
104 | Env = $_.Name
105 | $VariableName = $_.variables.$VariableName.value
106 | }
107 | }
108 | }
109 | }
110 | }
111 | }
112 | }
113 |
114 | $releases | Sort-Object -Property Collection,Project | Format-Table -AutoSize
--------------------------------------------------------------------------------
/TFS/ReleaseVariables/Readme.md:
--------------------------------------------------------------------------------
1 | # Get-ReleasesWithVariables
2 | ## Synopsis
3 | Lists all releases with all environments that contain a specific variable.
4 | ## DESCRIPTION
5 | Iterates one or more ProjectCollections and lists all environments in all release definitions in all projects. If the environment contains a CertificateThumbprint it will be displayed as well.
6 | ## EXAMPLE
7 | Get-ReleasesWithVariable https:///tfs/ MyVariable
8 | ## EXAMPLE
9 | Get-ReleasesWithVariable -ProjectCollections @("https://m/tfs/", "https://m/tfs/") -VariableName MyVariable -verbose
10 | ## EXAMPLE
11 | Get-ReleasesWithVariable -AccountName VSTSAccountName -AccessToken PAT -VariableName MyVariable
12 |
13 | # Update-ReleaseVariables
14 | ## Synopsis
15 | Updates a variable with a given value in all release definitions.
16 | ## DESCRIPTION
17 | If you have specific values (like server names or certificate thumbprints) in a lot of release definitions, you may need to update them at once.
18 | The script iterates all projects and checks for a value and replaces it.
19 |
20 | Use -whatif to test without updating the values.
21 | ## EXAMPLE
22 | Update-ReleaseVariables.ps1 -AccountName -Accesstoken -VariableName -OldValues -NewValues -Verbose -WhatIf
23 | ## EXAMPLE
24 | Update-ReleaseVariables.ps1 -AccountName -Accesstoken -VariableName -OldValues @("old1", "old2") -NewValues @("new1", "new2") -Verbose -WhatIf
--------------------------------------------------------------------------------
/TFS/ReleaseVariables/Update-ReleaseVariables.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Updates a variable with a given value in all release definitions.
4 | .DESCRIPTION
5 | If you have specific values (like server names or certificate thumbprints) in a lot of release definitions, you may need to update them at once.
6 | The script iterates all projects and checks for a value and replaces it.
7 |
8 | Use -whatif to test without updating the values.
9 | .EXAMPLE
10 | Update-ReleaseVariables.ps1 -AccountName -Accesstoken -VariableName -OldValues -NewValues -Verbose -WhatIf
11 | .EXAMPLE
12 | Update-ReleaseVariables.ps1 -AccountName -Accesstoken -VariableName -OldValues @("old1", "old2") -NewValues @("new1", "new2") -Verbose -WhatIf
13 | #>
14 | [CmdletBinding(SupportsShouldProcess=$true)]
15 | Param
16 | (
17 | # One or more URLs to project collections on your on premise TFS.
18 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='TFS')]
19 | [string[]]
20 | $ProjectCollections,
21 |
22 | # The name of the VSTS account
23 | [Parameter(Mandatory=$true, Position=0, ParameterSetName='VSTS')]
24 | [string]
25 | $AccountName,
26 |
27 | # The private access token (PAT). See https://www.visualstudio.com/en-us/docs/setup-admin/team-services/use-personal-access-tokens-to-authenticate
28 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='VSTS')]
29 | $Accesstoken,
30 |
31 | # The name of the variable to search for.
32 | [Parameter(Mandatory=$true, Position=1, ParameterSetName='TFS')]
33 | [Parameter(Mandatory=$true, Position=2, ParameterSetName='VSTS')]
34 | [ValidateNotNullOrEmpty()]
35 | [string]
36 | $VariableName,
37 |
38 | # The old values for the variable
39 | [Parameter(Mandatory=$true, Position=2, ParameterSetName='TFS')]
40 | [Parameter(Mandatory=$true, Position=3, ParameterSetName='VSTS')]
41 | [string[]]
42 | $OldValues,
43 |
44 | # The new values for the variable
45 | [Parameter(Mandatory=$true, Position=3, ParameterSetName='TFS')]
46 | [Parameter(Mandatory=$true, Position=4, ParameterSetName='VSTS')]
47 | [string[]]
48 | $NewValues
49 | )
50 |
51 | if ($OldValues.Count -ne $NewValues.Count){
52 | throw "You must supply the same number for old and new values."
53 | }
54 |
55 | if ($PSCmdlet.ParameterSetName -eq "TFS")
56 | {
57 | # TFS On Premise
58 | $apiVersion = "2.2-preview.1"
59 | $additionalParameters = @{ UseDefaultCredentials = $null }
60 | }
61 | else
62 | {
63 | # VSTS
64 | $passkey = ":$($Accesstoken)"
65 | $encodedKey = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passkey))
66 | $token = "Basic $encodedKey"
67 | $apiVersion = "3.0-preview.2"
68 |
69 | $ProjectCollections = "https://$AccountName.visualstudio.com"
70 |
71 | $additionalParameters = @{ Headers = @{ Authorization = $token } }
72 | }
73 |
74 | $releases = @()
75 |
76 | function Update-EnvironmentVariable
77 | {
78 | [CmdletBinding(SupportsShouldProcess=$true)]
79 | Param
80 | (
81 | # The url to the project (i.e. https://tfs.hugoboss.com/tfs/HBCollaboration/VisitorManagement)
82 | [Parameter(Mandatory=$true, Position=0)]
83 | [string]
84 | $ProjectUrl,
85 |
86 | # The id of the build definition (i.e. 1)
87 | [Parameter(Mandatory=$true, Position=1)]
88 | [int]
89 | $DefinitionId,
90 |
91 | # The id of the environment (i.e. 2)
92 | [Parameter(Mandatory=$true, Position=2)]
93 | [int]
94 | $EnvironmentId,
95 |
96 | # The name of the variable to update (i.e. CertificateThumbprint)
97 | [Parameter(Mandatory=$true, Position=3)]
98 | [string]
99 | $VariableName,
100 |
101 | # The new value for the variable
102 | [Parameter(Mandatory=$true, Position=4)]
103 | [string]
104 | $VariableValue,
105 |
106 | $additionalParameters
107 | )
108 |
109 | $url = "$ProjectUrl/_apis/release/definitions/$($DefinitionId)?api-version=$ApiVersion"
110 |
111 | $definition = Invoke-RestMethod $url -Method Get @additionalParameters
112 |
113 | $env = $definition.environments | ? { $_.id -eq $EnvironmentId }
114 |
115 | $currentValue = $env.variables.$VariableName.value
116 |
117 | $env.variables.$VariableName.value = $VariableValue
118 |
119 | Write-Verbose "Change value of '$VariableName' in environment '$($env.name)' from '$currentValue' to '$VariableValue'."
120 |
121 | $url = "$ProjectUrl/_apis/release/definitions/$($DefinitionId)?api-version=$ApiVersion"
122 |
123 | $body = ConvertTo-Json -InputObject $definition -Depth 100 -Compress
124 |
125 | if ($pscmdlet.ShouldProcess("$url", "Update"))
126 | {
127 | $result = Invoke-RestMethod $url -Method Put -Body $body -ContentType "application/json" @additionalParameters
128 | Write-Verbose "Updated '$url' to revision '$($result.revision)"
129 | }
130 | }
131 |
132 |
133 | $ProjectCollections | % {
134 |
135 | $projectCollection = $_
136 | Write-Verbose "Processing ProjectCollection '$projectCollection'..."
137 |
138 | $projectsResult = Invoke-RestMethod "$projectCollection/_apis/projects?`$top=1000api-version=1.0" -Method Get @additionalParameters
139 |
140 | if ($PSCmdlet.ParameterSetName -eq "VSTS")
141 | {
142 | $projectCollection = "https://$AccountName.vsrm.visualstudio.com"
143 | }
144 |
145 | $projectsResult.value | % {
146 |
147 | $project = $_.name
148 |
149 | Write-Verbose "Processing project '$project'..."
150 |
151 | $url = "$ProjectCollection/$project/_apis/release/definitions?`$expand=environments&api-version=$ApiVersion"
152 |
153 | $result = Invoke-RestMethod $url -Method Get @additionalParameters
154 |
155 | if ($result.count -gt 0){
156 |
157 | $result.value | % {
158 |
159 | $url = "$ProjectCollection/$project/_apis/release/definitions/$($_.id)?api-version=$ApiVersion"
160 |
161 | $definition = Invoke-RestMethod $url -Method Get @additionalParameters
162 |
163 | $definitionName = $_.Name
164 | $definitionId = $_.Id
165 |
166 | $definition.environments | % {
167 |
168 | $environmentId = $_.id
169 | $environmentName = $_.Name
170 |
171 | Write-Verbose "Process environment '$environmentName' ($environmentId) in definition '$definitionName' ($definitionId)..."
172 |
173 | $oldValue = $_.variables.$VariableName.value
174 |
175 | if ($OldValues.Contains($oldValue)){
176 |
177 | $newValue = $NewValues[$OldValues.IndexOf($oldValue)]
178 |
179 | Write-Verbose "Found old value '$oldValue' in environemnt. Replace it with '$newValue'"
180 |
181 | Update-EnvironmentVariable -ProjectUrl "$ProjectCollection/$project" -DefinitionId $definitionId -EnvironmentId $environmentId -VariableName $VariableName -VariableValue $newValue -additionalParameters $additionalParameters
182 |
183 | $releases += [pscustomobject]@{
184 | Collection = $ProjectCollection
185 | Project = $project
186 | Name = $definitionName
187 | Env = $_.Name
188 | Match = $oldValue
189 | Replacement = $newValue
190 | }
191 |
192 | }
193 | else
194 | {
195 | Write-Verbose "No matching value found in environment."
196 | }
197 | }
198 | }
199 | }
200 | }
201 | }
202 |
203 | $releases | Sort-Object -Property Collection,Project -Descending | Format-Table -AutoSize
--------------------------------------------------------------------------------
/TFSBuild/README.md:
--------------------------------------------------------------------------------
1 | # TFS Build Scripts
2 | A solution for build scripts that can be used in vNext build tasks.
3 |
4 | ## AssemblyVersion
5 | A build script that can be included in TFS 2015 or Visual Studio Online (VSO) vNevt builds that update the version of all assemblies in a workspace.
6 | It uses the name of the build to extract the version number and updates all AssemblyInfo.cs files to use the new version.
7 |
8 | Add the variables MajorVersion and MinorVersion to your build. Set the Build number format (Tab General) to:
9 |
10 | $(BuildDefinitionName)_$(MajorVersion).$(MinorVersion)_$(Year:yy)$(DayOfYear)$(rev:.rr)
11 |
12 |
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "AssemblyVersion", "TFSBuild\AssemblyVersion\AssemblyVersion.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D8006F12-363A-4070-B528-80A80A07CD60}"
9 | ProjectSection(SolutionItems) = preProject
10 | README.md = README.md
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | EndGlobal
28 |
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild/AssemblyVersion/AssemblyVersion.pssproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | 2.0
6 | 6CAFC0C6-A428-4d30-A9F9-700E829FEA51
7 | Exe
8 | MyApplication
9 | MyApplication
10 | AssemblyVersion
11 |
12 |
13 | true
14 | full
15 | false
16 | bin\Debug\
17 | DEBUG;TRACE
18 | prompt
19 | 4
20 |
21 |
22 | pdbonly
23 | true
24 | bin\Release\
25 | TRACE
26 | prompt
27 | 4
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild/AssemblyVersion/README.md:
--------------------------------------------------------------------------------
1 | # Set assembly and app versions in TFS team build
2 | ## Introduction
3 | One of the most common customizations in TFS XAML build templates was to automatically update the
4 | assembly version number. This can also be done in build vNext using a small power shell script.
5 |
6 | I also added the option to apply the version to SharePoint / Office 365 apps.
7 |
8 | ## Description
9 | Please see [my post on writeabout.net]("http://writeabout.net/2015/11/01/set-assembly-and-app-version-to-a-matching-build-name-in-tfs-2015-or-vso-build-vnext/") for more
10 | details how to use the script in you build.
11 |
12 |
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild/AssemblyVersion/Set-AssemblyVersion/Set-AssemblyVersion.Tests.ps1:
--------------------------------------------------------------------------------
1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path
2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
3 | . "$here\$sut"
4 |
5 |
6 | Describe "Set-AssemblyVersion" {
7 |
8 | context "Given the SourceDirectory does not exit, it"{
9 |
10 | It "throws an exception"{
11 | { Set-AssemblyVersion -SourceDirectory "$TestDrive\XYZ" -BuildNumber "Dev_1.0.20150922.01" } | Should throw
12 | }
13 | }
14 |
15 | context "Given the SourceDirectory does exit, it"{
16 | setup -File "SourceDir\Properties\AssemblyInfo.cs" -Content "[assembly: AssemblyVersion(""1.0.0.0"")]"
17 |
18 | It "does not throw an exception"{
19 | { Set-AssemblyVersion -SourceDirectory "$TestDrive\SourceDir" -BuildNumber "Dev_1.0.20150922.01" } | Should not Throw
20 | }
21 |
22 | $content = Get-Content "$TestDrive\SourceDir\Properties\AssemblyInfo.cs"
23 |
24 | It "sets the version to the extracted verion of the build number"{
25 | $content | should match "[assembly: AssemblyVersion(""1.0.20150922.01"")]"
26 | }
27 | }
28 |
29 | context "Given the SourceDirectory does exit, it"{
30 | setup -File "SourceDir\Properties\AssemblyInfo.cs" -Content "[assembly: AssemblyVersion(""1.0.0.0"")]"
31 | Mock Get-AppManifest { return Get-Item "$TestDrive\SourceDir\Properties\AssemblyInfo.cs" } -Verifiable
32 | Mock Set-AppManifest -Verifiable
33 |
34 | Set-AssemblyVersion -SourceDirectory "$TestDrive\SourceDir" -BuildNumber "Dev_1.0.20150922.01" -SetAppVersion
35 |
36 | It "sets the version for apps, if switch is present"{
37 |
38 | Assert-MockCalled Get-AppManifest -Times 1
39 | Assert-MockCalled Set-AppManifest -Times 1
40 | }
41 | }
42 | }
43 |
44 | Describe "Get-Version"{
45 |
46 | context "Given a valid regex, it"{
47 |
48 | It "returns the expected version if one match was found"{
49 | $actual = Get-Version -BuildNumber "Build HelloWorld_0000.00.00.0" -VersionFormat "\d+\.\d+\.\d+\.\d+"
50 |
51 | $actual | Should be "0000.00.00.0"
52 | }
53 |
54 | It "returns the first result, if two results were found"{
55 | $actual = Get-Version -BuildNumber "1111.00.00.0_Build HelloWorld_0000.00.00.0" -VersionFormat "\d+\.\d+\.\d+\.\d+"
56 |
57 | $actual | Should be "1111.00.00.0"
58 | }
59 |
60 | It "throws an exception, if no match could be found"{
61 |
62 | { Get-Version -BuildNumber "Build HelloWorld_0" -VersionFormat "\d+\.\d+\.\d+\.\d+" } | Should throw
63 | }
64 | }
65 |
66 | context "Given an invalid regex, it"{
67 |
68 | It "throws an exception."{
69 |
70 | { Get-Version -BuildNumber "Build HelloWorld_0" -VersionFormat "lala" } | Should throw
71 | }
72 | }
73 | }
74 |
75 | Describe "Get-Files"{
76 |
77 | context "Given two projects in a solution folder, it"{
78 |
79 | setup -File "SourceDir\SolutionDir\Project1\Properties\AssemblyInfo.cs"
80 | setup -File "SourceDir\SolutionDir\Project2\Properties\AssemblyInfo.cs"
81 |
82 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
83 |
84 | It "returns two Assembly.cs files"{
85 | $actual | should not BeNullOrEmpty
86 | $actual.Count | should be 2
87 | }
88 | }
89 |
90 | context "Given two projects in different subfolders, it"{
91 |
92 | setup -File "SourceDir\SolutionDir\Subfolder1\Project1\Properties\AssemblyInfo.cs"
93 | setup -File "SourceDir\SolutionDir\Sub1\Sub2\Project2\Properties\AssemblyInfo.cs"
94 |
95 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
96 |
97 | It "returns two Assembly.cs files"{
98 | $actual | should not BeNullOrEmpty
99 | $actual.Count | should be 2
100 | }
101 | }
102 |
103 | context "Given a visual basic project, it"{
104 |
105 | setup -File "SourceDir\Properties\AssemblyInfo.vb"
106 |
107 | $actual = Get-Files -SourceDir $TestDrive\SourceDir
108 |
109 | It "returns two Assembly.cs files"{
110 | $actual | should not BeNullOrEmpty
111 | $actual.Count | should be 1
112 | }
113 | }
114 | }
115 |
116 | Describe "Set-FileContent"{
117 |
118 | context "Given an AssemblyInfo.cs file, it"{
119 |
120 | setup -File "SourceDir\AssemblyInfo.cs" -Content "//comment`r`n[assembly: AssemblyVersion(""1.0.0.0"")]`r`n[assembly: AssemblyFileVersion(""1.0.0.0"")]`r`n"
121 |
122 | $file = Get-Item "$TestDrive\SourceDir\AssemblyInfo.cs"
123 |
124 | Set-FileContent $file "15.88.99.17" "\d+\.\d+\.\d+\.\d+"
125 |
126 | $content = Get-Content "$TestDrive\SourceDir\AssemblyInfo.cs"
127 |
128 | It "sets the Version to the given version"{
129 | $content[1] | should match "[assembly: AssemblyVersion(""15.88.99.1"")]"
130 | }
131 |
132 | It "sets the FileVersion to the given version"{
133 | $content[2] | should match "[assembly: AssemblyFileVersion(""15.88.99.1"")]"
134 | }
135 | }
136 | }
137 |
138 | Describe "Get-AppManifest"{
139 |
140 | context "Given two apps in different subfolders, it"{
141 |
142 | setup -File "SourceDir\SolutionDir\Subfolder1\Project1\AppManifest.xml"
143 | setup -File "SourceDir\SolutionDir\Sub1\Sub2\Project2\AppManifest.xml"
144 |
145 | $actual = Get-AppManifest -SourceDir $TestDrive\SourceDir
146 |
147 | It "returns two app manifest files"{
148 | $actual | should not BeNullOrEmpty
149 | $actual.Count | should be 2
150 | }
151 | }
152 | }
153 |
154 | Describe "Set-AppManifest"{
155 |
156 | context "Given an app manifest file, it"{
157 |
158 | $content = "
159 |
165 |
166 | CalculatorApp
167 | ~remoteAppUrl/?{StandardTokens}
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 | "
177 |
178 | setup -File "SourceDir\AppManifest.xml" -Content $content
179 |
180 | $file = Get-Item "$TestDrive\SourceDir\AppManifest.xml"
181 |
182 | Set-AppManifest $file "9.9.9.9"
183 |
184 | [xml]$result = Get-Content $file
185 |
186 | It "sets the version attribute to the desired value."{
187 | $result.App.Version | should be "9.9.9.9"
188 | }
189 | }
190 | }
191 |
192 |
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild/AssemblyVersion/Set-AssemblyVersion/Set-AssemblyVersion.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Sets the assembly version of all assemblies in the source directory.
4 | .DESCRIPTION
5 | A build script that can be included in TFS 2015 or Visual Studio Online (VSO) vNevt builds that update the version of all assemblies in a workspace.
6 | It uses the name of the build to extract the version number and updates all AssemblyInfo.cs files to use the new version.
7 | .EXAMPLE
8 | Set-AssemblyVersion
9 | .EXAMPLE
10 | Set-AssemblyVersion -SourceDirectory $Env:BUILD_SOURCESDIRECTORY -BuildNumber $Env:BUILD_BUILDNUMBER
11 | .EXAMPLE
12 | Set-AssemblyVersion -SourceDirectory ".\SourceDir" -BuildNumber "Dev_1.0.20150922.01" -VersionFormat "\d+\.\d+\.\d+\.\d+"
13 | #>
14 |
15 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
16 | [Alias()]
17 | [OutputType([int])]
18 | Param
19 | (
20 | # The path to the source directory. Default $Env:BUILD_SOURCESDIRECTORY is set by TFS.
21 | [Parameter(Mandatory=$false, Position=0)]
22 | [ValidateNotNullOrEmpty()]
23 | [string]$SourceDirectory = $Env:BUILD_SOURCESDIRECTORY,
24 |
25 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
26 | [Parameter(Mandatory=$false, Position=1)]
27 | [ValidateNotNullOrEmpty()]
28 | [string]$BuildNumber = $Env:BUILD_BUILDNUMBER,
29 |
30 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
31 | [Parameter(Mandatory=$false, Position=2)]
32 | [ValidateNotNullOrEmpty()]
33 | [string]$VersionFormat = "\d+\.\d+\.\d+\.\d+",
34 |
35 | # Set the version number also in all AppManifest.xml files.
36 | [Parameter(Mandatory=$false)]
37 | [switch]$SetAppVersion
38 | )
39 |
40 | <#
41 | .Synopsis
42 | Sets the assembly version of all assemblies in the source directory.
43 | .DESCRIPTION
44 | A build script that can be included in TFS 2015 or Visual Studio Online (VSO) vNevt builds that update the version of all assemblies in a workspace.
45 | It uses the name of the build to extract the version number and updates all AssemblyInfo.cs files to use the new version.
46 | .EXAMPLE
47 | Set-AssemblyVersion
48 | .EXAMPLE
49 | Set-AssemblyVersion -SourceDirectory $Env:BUILD_SOURCESDIRECTORY -BuildNumber $Env:BUILD_BUILDNUMBER
50 | .EXAMPLE
51 | Set-AssemblyVersion -SourceDirectory ".\SourceDir" -BuildNumber "Dev_1.0.20150922.01" -VersionFormat "\d+\.\d+\.\d+\.\d+"
52 | #>
53 | function Set-AssemblyVersion
54 | {
55 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
56 | [Alias()]
57 | [OutputType([int])]
58 | Param
59 | (
60 | # The path to the source directory. Default $Env:BUILD_SOURCESDIRECTORY is set by TFS.
61 | [Parameter(Mandatory=$false, Position=0)]
62 | [ValidateNotNullOrEmpty()]
63 | [string]$SourceDirectory = $Env:BUILD_SOURCESDIRECTORY,
64 |
65 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
66 | [Parameter(Mandatory=$false, Position=1)]
67 | [ValidateNotNullOrEmpty()]
68 | [string]$BuildNumber = $Env:BUILD_BUILDNUMBER,
69 |
70 | # The build number. Default $Env:BUILD_BUILDNUMBER is set by TFS and must be configured according your regex.
71 | [Parameter(Mandatory=$false, Position=2)]
72 | [ValidateNotNullOrEmpty()]
73 | [string]$VersionFormat = "\d+\.\d+\.\d+\.\d+",
74 |
75 | # Set the version number also in all AppManifest.xml files.
76 | [Parameter(Mandatory=$false)]
77 | [switch]$SetAppVersion
78 | )
79 |
80 | if (-not (Test-Path $SourceDirectory)) {
81 | throw "The directory '$SourceDirectory' does not exist."
82 | }
83 |
84 | $Version = Get-Version -BuildNumber $BuildNumber -VersionFormat $VersionFormat
85 |
86 | $files = Get-Files -SourceDirectory $SourceDirectory
87 |
88 | Set-FileContent -Files $files -Version $Version -VersionFormat $VersionFormat
89 |
90 | if ($SetAppVersion.IsPresent)
91 | {
92 | $files = Get-AppManifest -SourceDirectory $SourceDirectory
93 | Set-AppManifest -Files $files -Version $Version
94 | }
95 | }
96 |
97 | function Get-Version
98 | {
99 | [CmdletBinding()]
100 | [Alias()]
101 | [OutputType([string])]
102 | Param
103 | (
104 | [Parameter(Mandatory=$true, Position=0)]
105 | [ValidateNotNullOrEmpty()]
106 | [string]$BuildNumber,
107 |
108 | [Parameter(Mandatory=$true, Position=1)]
109 | [ValidateNotNullOrEmpty()]
110 | [string]$VersionFormat
111 | )
112 |
113 | $VersionData = [regex]::matches($BuildNumber,$VersionFormat)
114 |
115 | if ($VersionData.Count -eq 0){
116 | throw "Could not find version number with format '$VersionFormat' in BUILD_BUILDNUMBER '$BuildNumber'."
117 | }
118 |
119 | return $VersionData[0]
120 | }
121 |
122 | function Get-Files
123 | {
124 | [CmdletBinding()]
125 | [Alias()]
126 | [OutputType([System.IO.FileSystemInfo[]])]
127 | Param
128 | (
129 | [Parameter(Mandatory=$true, Position=0)]
130 | [ValidateNotNullOrEmpty()]
131 | [string]$SourceDirectory
132 | )
133 |
134 | $folders = Get-ChildItem $SourceDirectory -Recurse -Include "*Properties*" | Where-Object { $_.PSIsContainer }
135 |
136 | return $folders | ForEach-Object { Get-ChildItem -Path $_.FullName -Recurse -include AssemblyInfo.* }
137 | }
138 |
139 | function Get-AppManifest
140 | {
141 | [CmdletBinding()]
142 | [Alias()]
143 | [OutputType([System.IO.FileSystemInfo[]])]
144 | Param
145 | (
146 | [Parameter(Mandatory=$true, Position=0)]
147 | [ValidateNotNullOrEmpty()]
148 | [string]$SourceDirectory
149 | )
150 |
151 | return Get-ChildItem -Path $SourceDirectory -Recurse -Filter "AppManifest.xml"
152 | }
153 |
154 | function Set-FileContent
155 | {
156 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
157 | [OutputType([int])]
158 | Param
159 | (
160 | [Parameter(Mandatory=$true, Position=0)]
161 | [System.IO.FileSystemInfo[]]$Files,
162 |
163 | [Parameter(Mandatory=$true, Position=1)]
164 | [string]$Version,
165 |
166 | [Parameter(Mandatory=$true, Position=2)]
167 | [string]$VersionFormat
168 | )
169 |
170 | foreach ($file in $Files)
171 | {
172 | $filecontent = Get-Content $file
173 |
174 | if ($PSCmdlet.ShouldProcess("$file", "Set-AssemblyVersion"))
175 | {
176 | attrib $file -r
177 | $filecontent -replace $VersionFormat, $Version | Out-File $file
178 | Write-Verbose -Message "Applied Version '$Version' $($file.FullName) - version applied"
179 | }
180 | }
181 | }
182 |
183 | function Set-AppManifest
184 | {
185 | [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
186 | [OutputType([int])]
187 | Param
188 | (
189 | [Parameter(Mandatory=$true, Position=0)]
190 | [System.IO.FileSystemInfo[]]$Files,
191 |
192 | [Parameter(Mandatory=$true, Position=1)]
193 | [string]$Version
194 | )
195 |
196 | foreach ($file in $Files)
197 | {
198 | [xml]$xml = Get-Content $file
199 |
200 | $xml.App.Version = $Version
201 |
202 | if ($PSCmdlet.ShouldProcess("$file", "Set-AppManifest")){
203 | $xml.Save($file.FullName)
204 | }
205 | }
206 | }
207 |
208 |
209 | if (-not ($myinvocation.line.Contains("`$here\`$sut"))) {
210 | Set-AssemblyVersion -SourceDirectory $SourceDirectory -BuildNumber $BuildNumber -VersionFormat $VersionFormat
211 | }
--------------------------------------------------------------------------------
/TFSBuild/TFSBuild/Start-Release/Start-Release.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .Synopsis
3 | Triggers a release from a build for Visual Studio Release Management 2015 on premises
4 | .DESCRIPTION
5 | Use this script in your builds to trigger a release in Visual Studio Release Management 2015 on premises.
6 | .EXAMPLE
7 | .\Start-Release.ps1 -ReleaseDefinition MyReleaseDefinition -ReleasePath MyPath -TargetStage Dev
8 | .EXAMPLE
9 | .\Start-Release.ps1 -ReleaseDefinition MyReleaseDefinition -ReleasePath MyPath
10 | #>
11 | [CmdletBinding()]
12 | [OutputType([int])]
13 | Param (
14 | # The name of the release definition
15 | [Parameter(Mandatory=$true, Position=0)]
16 | [ValidateNotNullOrEmpty()]
17 | [string]$ReleaseDefinition,
18 |
19 | # The name of the release path
20 | [Parameter(Mandatory=$true, Position=1)]
21 | [ValidateNotNullOrEmpty()]
22 | [string]$ReleasePathName,
23 |
24 | # The build number. This is automatically set by the build.
25 | [Parameter(Mandatory=$false, Position=2)]
26 | [ValidateNotNullOrEmpty()]
27 | [string]$BuildNumber = $env:TF_BUILD_BUILDNUMBER,
28 |
29 | # The target stage. Adjust the validation set acording to your values
30 | # from the Administration / Manage Picklist / Stage Type in RM Client...
31 | [Parameter(Mandatory=$false, Position=2)]
32 | [ValidateSet("Dev", "Test", "QA", "Prod", "Last")]
33 | [string]$TargetStage = "Last",
34 |
35 | # The api version of the rest api. This might change with later updates.
36 | # Use fiddler to check the version that is used by rm client.
37 | [string]$ApiVersion = "6.0"
38 | )
39 |
40 | # Set the name of your release management server and the port (default 1000)
41 | $serverName = ":1000"
42 | $releaseManagementService = "http://$serverName/account/releaseManagementService/_apis/releaseManagement"
43 |
44 | # Get the ReleasePathId
45 | $releasePaths = Invoke-RestMethod "$releaseManagementService/ReleaseDefinitionService/ListReleaseDefinitions?api-version=$ApiVersion" -UseDefaultCredentials -Method Post
46 | $releasePathId = $releasePaths.ApplicationVersionList.ApplicationVersion | ? { $_.Name -eq $ReleasePathName } | % { $_.ReleasePathId }
47 |
48 | # Get the TargetStageId
49 | $releasePath = Invoke-RestMethod "$releaseManagementService/ConfigurationService/GetReleasePath?id=$releasePathId&api-version=$ApiVersion" -UseDefaultCredentials -Method Post
50 | $stages = $releasePath.ReleasePath.Stages.Stage
51 |
52 | if ($TargetStage -ne "Last") {
53 | $targetStageId = $stages | ? { $_.StageTypeName -eq $TargetStage } | % { $_.Id }
54 | }else {
55 | $targetStageId = $stages | Select-Object -Last 1 | % { $_.Id }
56 | }
57 |
58 | # Create the release name
59 | $releaseName = "Release: $(Get-Date -Format G)"
60 |
61 | # Create the property bag
62 | $deploymentPropertyBag = "{""ReleaseName"" : ""$releaseName"",""ReleaseBuild"": ""$BuildNumber"",""ReleaseBuildChangeset"" : """",""TargetStageId"": ""$targetStageId"",""ProviderHostedApp:Build"" : """",""ProviderHostedApp:BuildChangesetRange"" : ""-1,-1""}"
63 | $propertyBag = [System.Uri]::EscapeDataString($deploymentPropertyBag)
64 |
65 | $uri = "$releaseManagementService/OrchestratorService/InitiateRelease?releaseTemplateName=$releaseDefinition&deploymentPropertyBag=$propertyBag&api-version=6.0"
66 |
67 | $result = Invoke-RestMethod -Uri $uri -Method Post -UseDefaultCredentials
68 |
69 | if ($result.ErrorMessage) {
70 | throw $result.ErrorMessage
71 | }
72 |
73 | return $result
--------------------------------------------------------------------------------