forked from gregnottageGHOrg/IntuneScripts
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNew-CMApp.ps1
415 lines (355 loc) · 18.8 KB
/
New-CMApp.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
<#
.SYNOPSIS
Create Config Manager Apps from the same Framework the the Intune APPs with the same detection clauses
.DESCRIPTION
Create Config Manager Apps from the same Framework the the Intune APPs with the same detection clauses
.EXAMPLE
C:\PS> New-CMApp.ps1
.EXAMPLE
C:\PS> New-CMApp.ps1 -packagePath "Microsoft-BGInf- 4.26" -DistributionPointGroupName "Alle Server" -SourceLocation "\\sccm01-2019\Intune-Apps$"
.EXAMPLE
C:\PS> \\cm01\SMS_Applications$\_Fabian\Intune-Apps\New-CMApp.ps1 -packagePath "Microsoft-BGInfo-4.26" -DistributionPointGroupName "PS1 Head Quarter DP Group 1" -SourceLocation "\\cm01\SMS_Applications$\_Fabian\Intune-Apps"
.EXAMPLE
C:\PS> \\CM01\Intune-Apps$\New-CMApp.ps1 -packagePath "Microsoft-BGInfo-4.26" -DistributionPointGroupName "PS1 Head Quarter DP Group 1" -SourceLocation "\\CM01\Intune-Apps$"
.PARAMETER packagePath
Subfolder for the Application package.
.PARAMETER DistributionPointGroupName
DistributionPointGroupName
.PARAMETER SourceLocation
UNC Path on Server for the Sources
.PARAMETER Required
The software package will be set as Required instead of available
.PARAMETER SCCMServer
Set SCCM server if this script is not started from the SCCM server
.PARAMETER CollectionName
Set the collection to assign the software package
.NOTES
Author : Fabian Niesen (www.fabian-niesen.de)
Filename : New-CMApp.ps1
Requires : PowerShell Version 3.0
Version : 0.5
History : 0.5 FN 24.03.2022 Add "-Required" to allow Required software packages, otherwise they will be available to the collection
0.4 FN 22.03.2022 Bugfixes and some clean up
0.3 FN 95.07.2021 Bugfixes and additional improvments - Thanks to Oliver Düsterhöft
0.2 FN 28.06.2021 initial version
.LINK
https://github.com/InfrastructureHeroes/Intune-Apps
.KnownBugs / ToDo
- Only PS1 is supported now, MSI and EXE needs to be added
- Switch for Required / Available
#>
[CmdLetBinding()]
param(
[Parameter(Mandatory = $true, Position = 1, ValueFromPipelineByPropertyName = $true,
ValueFromPipeline = $True,
HelpMessage = 'Please enter path to package folder, containing Config.xml file'
)]
[Alias("PackageName")]
[string[]] $packagePath,
[Parameter(Mandatory = $false, Position = 2, ValueFromPipelineByPropertyName = $true,
ValueFromPipeline = $True,
HelpMessage = 'Please enter Distribution Point Group Name'
)]
[string] $DistributionPointGroupName = "Alle Server",
[Parameter(Mandatory = $false, Position = 3, ValueFromPipelineByPropertyName = $true,
ValueFromPipeline = $True,
HelpMessage = 'Please enter UNC for IntuneApp Folder'
)]
[string] $SourceLocation = "\\sccm01-2019\Intune-Apps-Beta$\",
[string] $SCCMServer = ([System.Net.Dns]::GetHostByName(($env:computerName))).Hostname,
[string] $CollectionName = "All Systems",
[switch] $required
)
####################################################
####################################################
#region Build Functions - Based on Upload-IntuneWin.ps1
<#
.COPYRIGHT
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
See LICENSE in the project root for license information.
Build Functions - Based on Upload-IntuneWin.ps1 - Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
#>
####################################################
Function Start-Log {
param (
[string]$FilePath,
[Parameter(HelpMessage = 'Deletes existing file if used with the -DeleteExistingFile switch')]
[switch]$DeleteExistingFile
)
# Some improvements bei Oliver Düsterhöft
Write-Verbose "Start-Log() - starting"
Write-Verbose "Start-Log() - FilePath ='$FilePath'"
Write-Verbose "Start-Log() - DeleteExistingFile ='$DeleteExistingFile'"
Write-Verbose "Start-Log() - logPath (Global) ='$logPath'"
Write-Verbose "Start-Log() - EventLogSource (Global) ='$EventLogSource'"
Write-Verbose "Start-Log() - ScriptLogFilePath (Script) ='$script:ScriptLogFilePath'"
#Create Event Log source if it's not already found...
If (!([system.diagnostics.eventlog]::SourceExists($EventLogSource))) { New-EventLog -LogName $EventLogName -Source $EventLogSource }
Try {
If (!(Test-Path $logPath))
{
Write-Verbose "Start-Log() - Create Log-Folder '$logPath'"
New-Item $logPath -ItemType Directory
}
else
{
Write-Verbose "Start-Log() - Log-Folder exists"
}
If ($DeleteExistingFile) {
Write-Host "Start-Log() - Delete-Existing-File '$FilePath' - if exists"
If (Test-Path $FilePath ) {
Write-Verbose "Start-Log() - Delete-Existing-File '$FilePath' - file found"
Remove-Item $FilePath -Force
If (!(Test-Path $FilePath) ) { Write-Verbose "Start-Log() - Delete-Existing-File '$FilePath' - file deleted" } }
}
If (!(Test-Path $FilePath) ) {
Write-Verbose "Start-Log() - Create the log file '$FilePath'"
New-Item $FilePath -Type File -Force | Out-Null
}
else { Write-Verbose "Start-Log() - Log-File exists" }
## Set the global variable to be used as the FilePath for all subsequent Write-Log
## calls in this session
$script:ScriptLogFilePath = $FilePath
}
Catch {
Write-Error $_.Exception.Message
}
Write-Verbose "Start-Log() - finished"
}
####################################################
Function Write-Log {
#Write-Log -Message 'warning' -LogLevel 2
#Write-Log -Message 'Error' -LogLevel 3
param (
[Parameter(Mandatory = $true)]
[string]$Message,
[Parameter()]
[ValidateSet(1, 2, 3)]
[int]$LogLevel = 1,
[Parameter(HelpMessage = 'Outputs message to Event Log,when used with -WriteEventLog')]
[switch]$WriteEventLog
)
Write-Host $Message
$TimeGenerated = "$(Get-Date -Format HH:mm:ss).$((Get-Date).Millisecond)+000"
$Line = '<![LOG[{0}]LOG]!><time="{1}" date="{2}" component="{3}" context="" type="{4}" thread="" file="">'
$LineFormat = $Message, $TimeGenerated, (Get-Date -Format MM-dd-yyyy), "$($MyInvocation.ScriptName | Split-Path -Leaf):$($MyInvocation.ScriptLineNumber)", $LogLevel
$Line = $Line -f $LineFormat
Add-Content -Value $Line -Path $ScriptLogFilePath
If ($WriteEventLog) { Write-EventLog -LogName $EventLogName -Source $EventLogSource -Message $Message -Id 100 -Category 0 -EntryType Information }
}
####################################################
function IsNull($objectToCheck) {
if ($objectToCheck -eq $null) {
return $true
}
if ($objectToCheck -is [String] -and $objectToCheck -eq [String]::Empty) {
return $true
}
if ($objectToCheck -is [DBNull] -or $objectToCheck -is [System.Management.Automation.Language.NullString]) {
return $true
}
return $false
}
####################################################
Function Get-XMLConfig {
<#
.SYNOPSIS
This function reads the supplied XML Config file
.DESCRIPTION
This function reads the supplied XML Config file
.EXAMPLE
Get-XMLConfig -XMLFile PathToXMLFile
This function reads the supplied XML Config file
.NOTES
NAME: Get-XMLConfig
#>
[cmdletbinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$XMLFile,
[bool]$Skip = $false
)
Begin {
Write-Log -Message "$($MyInvocation.InvocationName) function..."
}
Process {
$dayDateTime = (Get-Date -UFormat "%A %d-%m-%Y %R")
If (-Not(Test-Path $XMLFile)) {
Write-Log -Message "Error - XML file not found: $XMLFile" -LogLevel 3
Return $Skip = $true
}
Write-Log -Message "Reading XML file: $XMLFile"
[xml]$script:XML_Content = Get-Content $XMLFile
ForEach ($XMLEntity in $XML_Content.GetElementsByTagName("Azure_Settings")) {
If (IsNull($Username)) {
$script:Username = [string]$XMLEntity.Username
}
$script:baseUrl = [string]$XMLEntity.baseUrl
$script:logRequestUris = [string]$XMLEntity.logRequestUris
$script:logHeaders = [string]$XMLEntity.logHeaders
$script:logContent = [string]$XMLEntity.logContent
$script:azureStorageUploadChunkSizeInMb = [string]$XMLEntity.azureStorageUploadChunkSizeInMb
$script:sleep = [int32]$XMLEntity.sleep
}
ForEach ($XMLEntity in $XML_Content.GetElementsByTagName("IntuneWin_Settings")) {
If ($script:AADGroupName.Length -gt 50) {
Write-Log -Message "Error - AAD group name longer than 50 chars. Shorten then retry."
Exit
}
$script:AppType = [string]$XMLEntity.AppType
If ( ( $AppType -eq "EXE" ) -or ( $AppType -eq "MSI" ) ) {
Write-Log -Message "Reading commands for AppType: $AppType"
$script:installCmdLine = [string]$XMLEntity.installCmdLine
$script:uninstallCmdLine = [string]$XMLEntity.uninstallCmdLine
}
If ( $AppType -eq "Edge" ) {
Write-Log -Message "Reading commands for AppType: $AppType"
$script:displayName = [string]$XMLEntity.displayName
$script:Description = [string]$XMLEntity.Description + "`nObject creation: $dayDateTime"
$script:Publisher = [string]$XMLEntity.Publisher
$script:Channel = [string]$XMLEntity.Channel
$script:AADGroupName = [string]$XMLEntity.AADGroupName
Return
}
$script:RuleType = [string]$XMLEntity.RuleType
If ($RuleType -eq "FILE") {
Write-Log -Message "Reading detection for RuleType: $RuleType"
$script:FilePath = [string]$XMLEntity.FilePath
}
$script:ReturnCodeType = [string]$XMLEntity.ReturnCodeType
$script:InstallExperience = [string]$XMLEntity.InstallExperience
$script:PackageName = [string]$XMLEntity.PackageName
$script:displayName = [string]$XMLEntity.displayName
$script:Description = [string]$XMLEntity.Description + "`nObject creation: $dayDateTime"
$script:Publisher = [string]$XMLEntity.Publisher
$script:Category = [string]$XMLEntity.Category
$script:LogoFile = [string]$XMLEntity.LogoFile
$script:AADGroupName = [string]$XMLEntity.AADGroupName
$script:Version = [string]$XMLEntity.Version
#Strip .ps1 extension, if entered into XML file...
$lastFourChars = $PackageName.Substring($PackageName.Length - 4)
If ($lastFourChars -eq ".ps1") { $script:PackageName = $PackageName.Substring(0, $PackageName.Length - 4) }
}
}
End {
If ($Skip) { Return }# Just return without doing anything else
Write-Log -Message "Returning..."
Return
}
}
####################################################
#>
#endregion Build Functions - Based on Upload-IntuneWin.ps1
##########################################################################################################
<#
.COPYRIGHT
Copyright (c) Fabian Niesen. All rights reserved. Licensed under the MIT license.
See LICENSE in the project root for license information.
#>
$runpath = Get-Location
IF ( $packagePath.StartsWith(".\") ) { $packagePath = $packagePath.TrimStart(".\") }
IF ( $packagePath.EndsWith("\") ) { $packagePath = $packagePath.TrimEnd("\") }
$ScriptName = $myInvocation.MyCommand.Name
$ScriptName = $ScriptName.Substring(0, $ScriptName.Length - 4)
Write-Verbose "ScriptName : $ScriptName"
#$ScriptSource = $myInvocation.MyCommand.Source.Substring(0, $ScriptName.Length + 5)
$ScriptSource = $PSScriptRoot
Write-Verbose "ScriptSource: $ScriptSource"
$rundate = Get-Date -format yyyyMMdd-HHmm
$LogName = $ScriptName + "_" + $packagePath + "_" + $rundate
#$logPath = "$($env:LocalAppData)\Microsoft\Temp\IntuneApps\$ScriptName"
#$logPath = "$($env:ProgramData)\Microsoft\IntuneApps\$ScriptName"
$logPath = $ScriptSource + "\_Logs"
$logFile = "$logPath\$LogName.log"
Write-Host "LogFile : $logFile"
$script:EventLogName = "Application"
$script:EventLogSource = "EventSystem"
Start-Log -FilePath $logFile
Write-Host "Script log file path is [$logFile]" -f Cyan
Write-Log -Message "Starting $ScriptName version $BuildVer" -WriteEventLog
If ( ! ( Test-Path ("FileSystem::$SourceLocation\$packagePath") ) ) { Write-Log -Message ("MAIN() - Error - path not valid: " + $SourceLocation + "\" + $packagePath) ; Exit }
Get-XMLConfig -XMLFile ("FileSystem::$SourceLocation\$packagePath\Config.xml")
If ( $script:AppType -notlike "PS1") { Write-Log -Message "Actual is only PS1 Deploments from Intune-Scripts supported, but feel free to commit a change to GitHub ;)" -LogLevel 3 ; Exit }
IF (!(get-psdrive -PSProvider CMSite))
{
Write-Log -Message "Import-Module ConfigurationManager"
#Import-Module ConfigurationManager
Import-Module "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1" -Verbose:$false
New-PSDrive -Name "SCCM" -PSProvider "AdminUI.PS.Provider\CMSite" -Root $SCCMServer -Description "Primary site"
}
Set-Location $(((get-psdrive -PSProvider CMSite).Name) + ":")
$site = $(Get-CMSite).SiteCode
Write-Log -Message "CM SiteCode: $site"
IF ( $(Get-CMDistributionPointGroup -Name $DistributionPointGroupName ).count -ne 1 )
{
Write-Log -Message "Error - DistributionPointGroupName not valid: DistributionPointGroupName"
Exit
}
$PackageName = $script:PackageName
$InstallCommand = 'Powershell.exe -ExecutionPolicy ByPass -File "' + $PackageName + '.ps1" -install'
$UninstallCommand = 'Powershell.exe -ExecutionPolicy ByPass -File "' + $PackageName + '.ps1" -uninstall'
$RebootBehavior = "NoAction"
Write-Log -Message "Setting detection clauses"
$clause1 = New-CMDetectionClauseRegistryKeyValue -Hive LocalMachine -KeyName "SOFTWARE\Microsoft\IntuneApps" -PropertyType String -ValueName "$PackageName" -Value -ExpectedValue "Installed" -ExpressionOperator Contains
$clause2 = New-CMDetectionClauseRegistryKeyValue -Hive CurrentUser -KeyName "SOFTWARE\Microsoft\IntuneApps" -PropertyType String -ValueName "$PackageName" -Value -ExpectedValue "Installed" -ExpressionOperator Contains
$clause3 = New-CMDetectionClauseFile -FileName $($PackageName + ".tag") -Path "%PROGRAMDATA%\Microsoft\IntuneApps\$PackageName" -Existence
$clause4 = New-CMDetectionClauseFile -FileName $($PackageName + ".tag") -Path "%LOCALAPPDATA%\Microsoft\IntuneApps\$PackageName" -Existence
#$clause1,$clause2,$clause3,$clause4 | FT -AutoSize
IF ($SourceLocation.EndsWith("\") -like "False") { $SourceLocation =$SourceLocation+"\" }
$ContentLocation = $SourceLocation + $packagePath + "\source\"
Write-Verbose "PackageName: $PackageName"
Write-Log -Message "InstallCommand: $InstallCommand"
Write-Log -Message "UninstallCommand: $UninstallCommand"
Write-Log -Message "New-CMApplication $PackageName"
IF ( ! ( Get-CMApplication -Name $PackageName )) #Detect different version is missing#
{
IF ( IsNull($script:Version) )
{
New-CMApplication -Name $PackageName -Description $script:Description -AutoInstall $true -Publisher $script:Publisher -ReleaseDate $(Get-Date) | Format-Table -Property LocalizedDisplayName,DateLastModified -AutoSize
}
ELSE
{
New-CMApplication -Name $PackageName -Description $script:Description -AutoInstall $true -Publisher $script:Publisher -ReleaseDate $(Get-Date) -SoftwareVersion $script:Version | Format-Table -Property LocalizedDisplayName,DateLastModified -AutoSize
}
$app = Get-CMApplication -Name $PackageName
IF ( ! (Test-Path $($site + ":\Application\Intune-Apps")) )
{
Write-Log -Message "Ceate CM Application folder for Intune-Apps"
New-Item -Path $($site + ":\Application\Intune-Apps")
}
Move-CMObject -FolderPath $($site + ":\Application\Intune-Apps") -InputObject $app
}
Else
{
Write-Log -Message "CMApplication $PackageName already exists - Skip Creation" -LogLevel 2
}
$DeploymentTypeName = "Install " + $PackageName
IF ( ! ( Get-CMDeploymentType -ApplicationName $PackageName | Where-Object { $_.LocalizedDisplayName -like $DeploymentTypeName } )) {
If ( $script:AppType -like "PS1")
{
Write-Log -Message "Add-CMScriptDeploymentType $DeploymentTypeName"
Add-CMScriptDeploymentType -ApplicationName $PackageName -DeploymentTypeName $DeploymentTypeName -InstallCommand $InstallCommand -LogonRequirementType WhetherOrNotUserLoggedOn -MaximumRuntimeMins 15 -UninstallCommand $UninstallCommand -RebootBehavior $RebootBehavior -AddDetectionClause $clause1,$clause2,$clause3,$clause4 -DetectionClauseConnector @{"LogicalName"=$clause1.Setting.LogicalName;"Connector"="OR"},@{"LogicalName"=$clause2.Setting.LogicalName;"Connector"="OR"},@{"LogicalName"=$clause3.Setting.LogicalName;"Connector"="OR"},@{"LogicalName"=$clause4.Setting.LogicalName;"Connector"="OR"} -ContentLocation $ContentLocation -InstallationBehaviorType InstallForSystem -SlowNetworkDeploymentMode Download -UserInteractionMode Normal -ContentFallback -EstimatedRuntimeMins 15 | Format-Table -Property LocalizedDisplayName,Technology -AutoSize
} ELSE {
Write-Log -Message "CMScriptDeploymentType $PackageName already exists - Skip Creation" -LogLevel 2 }
}
### Add other Deployment Types here ###
Write-Log -Message "New-CMApplicationDeployment for $PackageName"
IF ( ! ( Get-CMApplicationDeployment -Name $PackageName -CollectionName $CollectionName ))
{
If ($required)
{
Write-log "Create new Deployment for $PackageName, assigned to Collection $CollectionName as Required"
New-CMApplicationDeployment -CollectionName "$CollectionName" -Name "$PackageName" -DeployAction Install -DeployPurpose Required -UserNotification DisplayAll -AvailableDateTime (get-date) -TimeBaseOn LocalTime -DistributeContent -DistributionPointGroupName $DistributionPointGroupName | Format-Table -Property ApplicationName,Enabled,StartTime
}
else {
Write-log "Create new Deployment for $PackageName, assigned to Collection $CollectionName as Available"
New-CMApplicationDeployment -CollectionName "$CollectionName" -Name "$PackageName" -DeployAction Install -DeployPurpose Available -UserNotification DisplayAll -AvailableDateTime (get-date) -TimeBaseOn LocalTime -DistributeContent -DistributionPointGroupName $DistributionPointGroupName | Format-Table -Property ApplicationName,Enabled,StartTime
}
}
ELSE
{
Write-Log -Message "CMApplicationDeployment $PackageName already exists - Skip Creation" -LogLevel 2
}
Set-Location $runpath
#Remove-PSDrive -Name $site -PSProvider "AdminUI.PS.Provider\CMSite"