Functions/New-IntuneWinPackage.psm1
function New-IntuneWinPackage { [cmdletbinding()] param ( [String]$SourcePath, [String]$GraphAPI = "https://graph.microsoft.com/beta", [Boolean]$CreateGroup = $false, [Boolean]$UserInstallation = $false, [Boolean]$ServiceUI = $false ) function WriteHost($Code, $Message) { if($code -eq 1){ Write-Host "[Pending] | " $Message -f Magenta } if($code -eq 2){ Write-Host "[Success] | " $Message -f DarkGreen } if($code -eq 3){ Write-Host "[Failed] | " $Message -f DarkRed } if($code -eq 4){ Write-Host "[Info] | " $Message -f DarkYellow } } #################################################### function Get-AuthToken { #Importing MSAL.PS if (!(Get-Module).Name.Contains('MSAL.PS')) { try { Write-Output -InputObject 'Loading Microsoft Authentication Library...' Import-Module -Name MSAL.PS -ErrorAction Stop Write-Output -InputObject 'Successfully loaded the Microsoft Authentication Library' } catch { Write-Output -InputObject 'We need to install it first...' Install-Module -Name MSAL.PS -Repository PSGallery -Scope CurrentUser Import-Module -Name MSAL.PS Write-Output -InputObject 'Successfully installed the Microsoft Authentication Library' } } #initiate the session variable $IntuneToken = @{ Name = 'IntuneToken' Description = 'Variable which will be used to managed the connection to Microsoft Graph' Scope = 'Global' Visibility = 'Public' Option = 'None' Value = @{ Success = $null Message = '' Authentication = $null ResultObject = $null Exception = $null Header = $null } } Set-Variable @IntuneToken $IntuneAuthParam = @{ clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547" Interactive = $true redirectUri = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547" } try { $IntuneAuthentication = Get-MsalToken @IntuneAuthParam -ErrorAction Stop $Global:IntuneToken.ResultObject = $IntuneAuthentication $Global:IntuneToken.Success = $true $Global:IntuneToken.Message = "Successfully retrieved bearer token" $Global:IntuneToken.Exception = $_ $IntuneAuthHeader = @{ 'Authorization' = "Bearer $($Global:IntuneToken.ResultObject.AccessToken)" 'Content-type' = 'application/json' 'ExpiresOn' = $Global:IntuneToken.ResultObject.ExpiresOn } $Global:IntuneToken.Header = $IntuneAuthHeader Write-Output -InputObject 'Successfully connected to Intune' $Global:IntuneToken.Authentication = @{ ClientID = ($Global:IntuneToken.ResultObject.ClaimsPrincipal.Claims | Where-Object { $_.Type -eq 'aud'}).Value TenantID = $Global:IntuneToken.ResultObject.TenantId Account = $Global:IntuneToken.ResultObject.Account.Username RedirectUri = $redirectUri } Write-Verbose -Message 'Stored the Authentication Information' return $Global:IntuneToken.Header } catch { $Global:IntuneToken.Success = $false $Global:IntuneToken.Message = "Error retrieving the bearer token" $Global:IntuneToken.Exception = $_ } } #################################################### function CloneObject($object){ $stream = New-Object IO.MemoryStream; $formatter = New-Object Runtime.Serialization.Formatters.Binary.BinaryFormatter; $formatter.Serialize($stream, $object); $stream.Position = 0; $formatter.Deserialize($stream); } #################################################### function WriteHeaders($authToken){ foreach ($header in $authToken.GetEnumerator()){ if ($header.Name.ToLower() -eq "authorization"){ continue; } Write-Host -ForegroundColor Gray "$($header.Name): $($header.Value)"; } } #################################################### function MakeGetRequest($collectionPath){ $uri = "$baseUrl$collectionPath"; $request = "GET $uri"; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $authToken; } try{ Test-AuthToken $response = Invoke-RestMethod $uri -Method Get -Headers $authToken; $response; } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function MakePatchRequest($collectionPath, $body){ MakeRequest "PATCH" $collectionPath $body; } #################################################### function MakePostRequest($collectionPath, $body){ MakeRequest "POST" $collectionPath $body; } #################################################### function MakeRequest($verb, $collectionPath, $body){ $uri = "$baseUrl$collectionPath"; $request = "$verb $uri"; $clonedHeaders = CloneObject $authToken; $clonedHeaders["content-length"] = $body.Length; $clonedHeaders["content-type"] = "application/json"; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $clonedHeaders; } if ($logContent) { Write-Host -ForegroundColor Gray $body; } try{ Test-AuthToken $response = Invoke-RestMethod $uri -Method $verb -Headers $clonedHeaders -Body $body; $response; } catch{ if ($_.Exception.Response.StatusCode.value__ -eq 401 -or $_.Exception.Response.StatusCode.value__ -eq 403){ Write-Host "------ 401/403 - Unauthorized ------" WriteHost 3 "Access Denied! Check your Endpoint Permissions or PIM activation" } if ($_.Exception.Response.StatusCode.value__ -eq 400){ Write-Host "------ 400 - Bad Request ------" WriteHost 3 "Check your Application.xml or Application.png!" } Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function UploadAzureStorageChunk($sasUri, $id, $body){ $uri = "$sasUri&comp=block&blockid=$id"; $request = "PUT $uri"; $iso = [System.Text.Encoding]::GetEncoding("iso-8859-1"); $encodedBody = $iso.GetString($body); $headers = @{ "x-ms-blob-type" = "BlockBlob" }; if ($logRequestUris) { Write-Host $request; } if ($logHeaders) { WriteHeaders $headers; } try{ Invoke-WebRequest $uri -Method Put -Headers $headers -Body $encodedBody | Out-Null } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function FinalizeAzureStorageUpload($sasUri, $ids){ $uri = "$sasUri&comp=blocklist"; $request = "PUT $uri"; $xml = '<?xml version="1.0" encoding="utf-8"?><BlockList>'; foreach ($id in $ids){ $xml += "<Latest>$id</Latest>"; } $xml += '</BlockList>'; if ($logRequestUris) { Write-Host $request; } if ($logContent) { Write-Host -ForegroundColor Gray $xml; } try{ Invoke-RestMethod $uri -Method Put -Body $xml; } catch{ Write-Host -ForegroundColor Red $request; Write-Host -ForegroundColor Red $_.Exception.Message; throw; } } #################################################### function UploadFileToAzureStorage($sasUri, $filepath, $fileUri){ try{ $chunkSizeInBytes = 1024l * 1024l * $azureStorageUploadChunkSizeInMb; $sasRenewalTimer = [System.Diagnostics.Stopwatch]::StartNew() $fileSize = (Get-Item $filepath).length; $chunks = [Math]::Ceiling($fileSize / $chunkSizeInBytes); $reader = New-Object System.IO.BinaryReader([System.IO.File]::Open($filepath, [System.IO.FileMode]::Open)); $reader.BaseStream.Seek(0, [System.IO.SeekOrigin]::Begin); $ids = @(); for ($chunk = 0; $chunk -lt $chunks; $chunk++){ $id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunk.ToString("0000"))); $ids += $id; $start = $chunk * $chunkSizeInBytes; $length = [Math]::Min($chunkSizeInBytes, $fileSize - $start); $bytes = $reader.ReadBytes($length); $currentChunk = $chunk + 1; Write-Progress -Activity "Uploading File to Azure Storage" -status "Uploading chunk $currentChunk of $chunks" ` -percentComplete ($currentChunk / $chunks*100) UploadAzureStorageChunk $sasUri $id $bytes; if ($currentChunk -lt $chunks -and $sasRenewalTimer.ElapsedMilliseconds -ge 450000){ RenewAzureStorageUpload $fileUri $sasRenewalTimer.Restart(); } } Write-Progress -Completed -Activity "Uploading File to Azure Storage" $reader.Close(); } finally { if ($null -ne $reader) { $reader.Dispose(); } } FinalizeAzureStorageUpload $sasUri $ids; } #################################################### function RenewAzureStorageUpload($fileUri){ $renewalUri = "$fileUri/renewUpload"; $actionBody = ""; MakePostRequest $renewalUri $actionBody; WaitForFileProcessing $fileUri "AzureStorageUriRenewal" $azureStorageRenewSasUriBackOffTimeInSeconds; } #################################################### function WaitForFileProcessing($fileUri, $stage){ $attempts= 600; $waitTimeInSeconds = 10; $successState = "$($stage)Success"; $pendingState = "$($stage)Pending"; $file = $null; while ($attempts -gt 0) { $file = MakeGetRequest $fileUri; if ($file.uploadState -eq $successState){ break; } elseif ($file.uploadState -ne $pendingState){ Write-Host -ForegroundColor Red $_.Exception.Message; throw "File upload state is not success: $($file.uploadState)"; } Start-Sleep $waitTimeInSeconds; $attempts--; } if ($null -eq $file -or $file.uploadState -ne $successState){ throw "File request did not complete in the allotted time."; } $file; } #################################################### function GetWin32AppBody(){ param ( [parameter(Mandatory=$true,ParameterSetName = "MSI",Position=1)] [Switch]$MSI, [parameter(Mandatory=$true,ParameterSetName = "EXE",Position=1)] [Switch]$EXE, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$displayName, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$publisher, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$description, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$filename, [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$SetupFileName, [parameter(Mandatory=$true)] [ValidateSet('system','user')] $installExperience = "system", [parameter(Mandatory=$true,ParameterSetName = "EXE")] [ValidateNotNullOrEmpty()] $installCommandLine, [parameter(Mandatory=$true,ParameterSetName = "EXE")] [ValidateNotNullOrEmpty()] $uninstallCommandLine, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiPackageType, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiProductCode, [parameter(Mandatory=$false,ParameterSetName = "MSI")] $MsiProductName, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiProductVersion, [parameter(Mandatory=$false,ParameterSetName = "MSI")] $MsiPublisher, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiRequiresReboot, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] $MsiUpgradeCode ) $AllowUninstall = $false #####falls App eine K-Software "Deinstallation ermöglichen" if ($($XMLsoftwaretype).contains("K-Software")) { $AllowUninstall = $true } $LogoBase64 = [Convert]::ToBase64String((Get-Content -Path $SourcePath\Application.png -Encoding Byte)) if($MSI){ #löschen $body = @{ "@odata.type" = "#microsoft.graph.win32LobApp" }; $body.applicableArchitectures = "x64,x86"; $body.description = $description; $body.developer = ""; $body.displayName = $displayName; $body.fileName = $filename; $body.installCommandLine = "msiexec /i `"$SetupFileName`"" $body.installExperience = @{"runAsAccount" = "$installExperience"}; $body.informationUrl = $null; $body.isFeatured = $false; $body.minimumSupportedOperatingSystem = @{"v10_1607" = $true}; $body.msiInformation = @{ "packageType" = "$MsiPackageType"; "productCode" = "$MsiProductCode"; "productName" = "$MsiProductName"; "productVersion" = "$MsiProductVersion"; "publisher" = "$MsiPublisher"; "requiresReboot" = "$MsiRequiresReboot"; "upgradeCode" = "$MsiUpgradeCode" }; $body.notes = ""; $body.allowAvailableUninstall = "$AllowUninstall"; $body.owner = ""; $body.privacyInformationUrl = $null; $body.publisher = $publisher; $body.runAs32bit = $false; $body.setupFilePath = $SetupFileName; $body.largeIcon = @{ "@odata.type" = "microsoft.graph.mimeContent"; "type" = "binary"; "value" = "$LogoBase64"; }; $body.uninstallCommandLine = "msiexec /x `"$MsiProductCode`"" } elseif($EXE){ if ($UserInstallation){ $installExperience = "user" } $date = Get-Date -Format "MM/dd/yyyy" $body = @{ "@odata.type" = "#microsoft.graph.win32LobApp" }; $body.description = "Created by: " + $XMLScriptAuthor; $body.developer = "WAGNER AG"; $body.displayName = $XMLvendor + " " + $XMLname + " " + $XMLversion + " " + $XMLlanguage + " " + $XMLRevision; $body.fileName = $filename; $body.installCommandLine = "$installCommandLine" $body.installExperience = @{"runAsAccount" = "$installExperience"}; $body.informationUrl = $null; $body.isFeatured = $false; $body.minimumSupportedOperatingSystem = @{"v10_1607" = $true}; $body.msiInformation = $null; $body.notes = $XMLsoftwaretype; $body.allowAvailableUninstall ="$AllowUninstall"; $body.owner = "WAGNER AG"; $body.privacyInformationUrl = $null; $body.publisher = $publisher; $body.runAs32bit = $false; $body.setupFilePath = $SetupFileName; $body.largeIcon = @{ "@odata.type" = "microsoft.graph.mimeContent"; "type" = "binary"; "value" = "$LogoBase64"; }; $body.uninstallCommandLine = "$uninstallCommandLine" } return $body } #################################################### function GetAppFileBody($name, $size, $sizeEncrypted, $manifest){ $body = @{ "@odata.type" = "#microsoft.graph.mobileAppContentFile" }; $body.name = $name; $body.size = $size; $body.sizeEncrypted = $sizeEncrypted; $body.manifest = $manifest; $body.isDependency = $false; return $body } #################################################### function GetAppCommitBody($contentVersionId, $LobType){ $body = @{ "@odata.type" = "#$LobType" }; $body.committedContentVersion = $contentVersionId; return $body } #################################################### Function Test-SourceFile(){ param ( [parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $SourceFile ) try { if(!(test-path "$SourceFile")){ Write-Host Write-Host "Source File '$sourceFile' doesn't exist..." -ForegroundColor Red throw } } catch { Write-Host -ForegroundColor Red $_.Exception.Message; Write-Host break } } #################################################### Function New-DetectionRule(){ [cmdletbinding()] param ( [parameter(Mandatory=$true,ParameterSetName = "PowerShell",Position=1)] [Switch]$PowerShell, [parameter(Mandatory=$true,ParameterSetName = "MSI",Position=1)] [Switch]$MSI, [parameter(Mandatory=$true,ParameterSetName = "File",Position=1)] [Switch]$File, [parameter(Mandatory=$true,ParameterSetName = "Registry",Position=1)] [Switch]$Registry, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] [String]$ScriptFile, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] $enforceSignatureCheck, [parameter(Mandatory=$true,ParameterSetName = "PowerShell")] [ValidateNotNullOrEmpty()] $runAs32Bit, [parameter(Mandatory=$true,ParameterSetName = "MSI")] [ValidateNotNullOrEmpty()] [String]$MSIproductCode, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateNotNullOrEmpty()] [String]$Path, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateNotNullOrEmpty()] [string]$FileOrFolderName, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateSet("notConfigured","exists","modifiedDate","createdDate","version","sizeInMB")] [string]$FileDetectionType, [parameter(Mandatory=$false,ParameterSetName = "File")] $FileDetectionValue = $null, [parameter(Mandatory=$true,ParameterSetName = "File")] [ValidateSet("True","False")] [string]$check32BitOn64System = "False", [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateNotNullOrEmpty()] [String]$RegistryKeyPath, [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateSet("notConfigured","exists","doesNotExist","string","integer","version")] [string]$RegistryDetectionType, [parameter(Mandatory=$false,ParameterSetName = "Registry")] [ValidateNotNullOrEmpty()] [String]$RegistryValue, [parameter(Mandatory=$true,ParameterSetName = "Registry")] [ValidateSet("True","False")] [string]$check32BitRegOn64System = "False" ) if($PowerShell){ if(!(Test-Path "$ScriptFile")){ Write-Host Write-Host "Could not find file '$ScriptFile'..." -ForegroundColor Red Write-Host "Script can't continue..." -ForegroundColor Red Write-Host break } $ScriptContent = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes("$ScriptFile")); $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppPowerShellScriptDetection" } $DR.enforceSignatureCheck = $false; $DR.runAs32Bit = $false; $DR.scriptContent = "$ScriptContent"; } elseif($MSI){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppProductCodeDetection" } $DR.productVersionOperator = "notConfigured"; $DR.productCode = "$MsiProductCode"; $DR.productVersion = $null; } elseif($File){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppFileSystemDetection" } $DR.check32BitOn64System = "$check32BitOn64System"; $DR.detectionType = "$FileDetectionType"; $DR.detectionValue = $FileDetectionValue; $DR.fileOrFolderName = "$FileOrFolderName"; $DR.operator = "notConfigured"; $DR.path = "$Path" } elseif($Registry){ $DR = @{ "@odata.type" = "#microsoft.graph.win32LobAppRegistryDetection" } $DR.check32BitOn64System = "$check32BitRegOn64System"; $DR.detectionType = "$RegistryDetectionType"; $DR.detectionValue = "Installed"; $DR.keyPath = "$RegistryKeyPath"; $DR.operator = "equal"; $DR.valueName = "$RegistryValue" } return $DR } #################################################### function Get-DefaultReturnCodes(){ @{"returnCode" = 0;"type" = "success"}, ` @{"returnCode" = 1707;"type" = "success"}, ` @{"returnCode" = 3010;"type" = "softReboot"}, ` @{"returnCode" = 1641;"type" = "hardReboot"}, ` @{"returnCode" = 1618;"type" = "retry"} } #################################################### function New-ReturnCode(){ param ( [parameter(Mandatory=$true)] [int]$returnCode, [parameter(Mandatory=$true)] [ValidateSet('success','softReboot','hardReboot','retry')] $type ) @{"returnCode" = $returnCode;"type" = "$type"} } #################################################### Function Get-IntuneWinXML(){ param ( [Parameter(Mandatory=$true)] $SourceFile, [Parameter(Mandatory=$true)] $fileName, [Parameter(Mandatory=$false)] [ValidateSet("false","true")] [string]$removeitem = "true" ) Test-SourceFile "$SourceFile" $Directory = [System.IO.Path]::GetDirectoryName("$SourceFile") Add-Type -Assembly System.IO.Compression.FileSystem $zip = [IO.Compression.ZipFile]::OpenRead("$SourceFile") $zip.Entries | Where-Object {$_.Name -like "$filename" } | ForEach-Object { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$Directory\$filename", $true) } $zip.Dispose() [xml]$IntuneWinXML = Get-Content "$Directory\$filename" return $IntuneWinXML if($removeitem -eq "true"){ remove-item "$Directory\$filename" } } #################################################### Function Get-IntuneWinFile(){ param ( [Parameter(Mandatory=$true)] $SourceFile, [Parameter(Mandatory=$true)] $fileName, [Parameter(Mandatory=$false)] [string]$Folder = "win32" ) $Directory = [System.IO.Path]::GetDirectoryName("$SourceFile") if(!(Test-Path "$Directory\$folder")){ New-Item -ItemType Directory -Path "$Directory" -Name "$folder" | Out-Null } Add-Type -Assembly System.IO.Compression.FileSystem $zip = [IO.Compression.ZipFile]::OpenRead("$SourceFile") $zip.Entries | Where-Object {$_.Name -like "$filename" } | ForEach-Object { [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$Directory\$folder\$filename", $true) } $zip.Dispose() return "$Directory\$folder\$filename" if($removeitem -eq "true"){ remove-item "$Directory\$filename" } } #################################################### function Add-Win32Lob(){ [cmdletbinding()] param ( [parameter(Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [string]$SourceFile, [parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$displayName, [parameter(Mandatory=$true,Position=2)] [ValidateNotNullOrEmpty()] [string]$publisher, [parameter(Mandatory=$true,Position=3)] [ValidateNotNullOrEmpty()] [string]$description, [parameter(Mandatory=$true,Position=4)] [ValidateNotNullOrEmpty()] $detectionRules, [parameter(Mandatory=$true,Position=5)] [ValidateNotNullOrEmpty()] $returnCodes, [parameter(Mandatory=$false,Position=6)] [ValidateNotNullOrEmpty()] [string]$installCmdLine, [parameter(Mandatory=$false,Position=7)] [ValidateNotNullOrEmpty()] [string]$uninstallCmdLine, [parameter(Mandatory=$false,Position=8)] [ValidateSet('system','user')] $installExperience = "system" ) try { $LOBType = "microsoft.graph.win32LobApp" Write-Host "Testing if SourceFile '$SourceFile' Path is valid..." -ForegroundColor Yellow Test-SourceFile "$SourceFile" Write-Host Write-Host "Creating JSON data to pass to the service..." -ForegroundColor Yellow $DetectionXML = Get-IntuneWinXML "$SourceFile" -fileName "detection.xml" if($displayName){ $DisplayName = $displayName } else { $DisplayName = $DetectionXML.ApplicationInfo.Name } $FileName = $DetectionXML.ApplicationInfo.FileName $SetupFileName = $DetectionXML.ApplicationInfo.SetupFile $Ext = [System.IO.Path]::GetExtension($SetupFileName) if((($Ext).contains("msi") -or ($Ext).contains("Msi")) -and (!$installCmdLine -or !$uninstallCmdLine)){ $MsiExecutionContext = $DetectionXML.ApplicationInfo.MsiInfo.MsiExecutionContext $MsiPackageType = "DualPurpose"; if($MsiExecutionContext -eq "System") { $MsiPackageType = "PerMachine" } elseif($MsiExecutionContext -eq "User") { $MsiPackageType = "PerUser" } $MsiProductCode = $DetectionXML.ApplicationInfo.MsiInfo.MsiProductCode $MsiProductVersion = $DetectionXML.ApplicationInfo.MsiInfo.MsiProductVersion $MsiPublisher = $DetectionXML.ApplicationInfo.MsiInfo.MsiPublisher $MsiRequiresReboot = $DetectionXML.ApplicationInfo.MsiInfo.MsiRequiresReboot $MsiUpgradeCode = $DetectionXML.ApplicationInfo.MsiInfo.MsiUpgradeCode if($MsiRequiresReboot -eq "false"){ $MsiRequiresReboot = $false } elseif($MsiRequiresReboot -eq "true"){ $MsiRequiresReboot = $true } $mobileAppBody = GetWin32AppBody ` -MSI ` -displayName "$DisplayName" ` -publisher "$publisher" ` -description $description ` -filename $FileName ` -SetupFileName "$SetupFileName" ` -installExperience $installExperience ` -MsiPackageType $MsiPackageType ` -MsiProductCode $MsiProductCode ` -MsiProductName $displayName ` -MsiProductVersion $MsiProductVersion ` -MsiPublisher $MsiPublisher ` -MsiRequiresReboot $MsiRequiresReboot ` -MsiUpgradeCode $MsiUpgradeCode } else { $mobileAppBody = GetWin32AppBody -EXE -displayName "$DisplayName" -publisher "$publisher" ` -description $description -filename $FileName -SetupFileName "$SetupFileName" ` -installExperience $installExperience -installCommandLine $installCmdLine ` -uninstallCommandLine $uninstallcmdline } if($DetectionRules.'@odata.type' -contains "#microsoft.graph.win32LobAppPowerShellScriptDetection" -and @($DetectionRules).'@odata.type'.Count -gt 1){ Write-Host Write-Warning "A Detection Rule can either be 'Manually configure detection rules' or 'Use a custom detection script'" Write-Warning "It can't include both..." Write-Host break } else { $mobileAppBody | Add-Member -MemberType NoteProperty -Name 'detectionRules' -Value $detectionRules } if($returnCodes){ $mobileAppBody | Add-Member -MemberType NoteProperty -Name 'returnCodes' -Value @($returnCodes) } else { Write-Host Write-Warning "Intunewin file requires ReturnCodes to be specified" Write-Warning "If you want to use the default ReturnCode run 'Get-DefaultReturnCodes'" Write-Host break } Write-Host Write-Host "Creating application in Intune..." -ForegroundColor Yellow try { $mobileApp = MakePostRequest "mobileApps" ($mobileAppBody | ConvertTo-Json); } catch { WriteHost 1 "Clear Session..." powershell -nologo exit } Write-Host Write-Host "Creating Content Version in the service for the application..." -ForegroundColor Yellow $global:appId = $mobileApp.id; $contentVersionUri = "mobileApps/$appId/$LOBType/contentVersions"; $contentVersion = MakePostRequest $contentVersionUri "{}"; Write-Host Write-Host "Getting Encryption Information for '$SourceFile'..." -ForegroundColor Yellow $encryptionInfo = @{}; $encryptionInfo.encryptionKey = $DetectionXML.ApplicationInfo.EncryptionInfo.EncryptionKey $encryptionInfo.macKey = $DetectionXML.ApplicationInfo.EncryptionInfo.macKey $encryptionInfo.initializationVector = $DetectionXML.ApplicationInfo.EncryptionInfo.initializationVector $encryptionInfo.mac = $DetectionXML.ApplicationInfo.EncryptionInfo.mac $encryptionInfo.profileIdentifier = "ProfileVersion1"; $encryptionInfo.fileDigest = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigest $encryptionInfo.fileDigestAlgorithm = $DetectionXML.ApplicationInfo.EncryptionInfo.fileDigestAlgorithm $global:fileEncryptionInfo = @{}; $global:fileEncryptionInfo.fileEncryptionInfo = $encryptionInfo; $IntuneWinFile = Get-IntuneWinFile "$SourceFile" -fileName "$filename" [int64]$Size = $DetectionXML.ApplicationInfo.UnencryptedContentSize $EncrySize = (Get-Item "$IntuneWinFile").Length Write-Host Write-Host "Creating a new file entry in Azure for the upload..." -ForegroundColor Yellow $contentVersionId = $contentVersion.id; $fileBody = GetAppFileBody "$FileName" $Size $EncrySize $null; $filesUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files"; $file = MakePostRequest $filesUri ($fileBody | ConvertTo-Json); Write-Host Write-Host "Waiting for the file entry URI to be created..." -ForegroundColor Yellow $fileId = $file.id; $fileUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files/$fileId"; $file = WaitForFileProcessing $fileUri "AzureStorageUriRequest"; Write-Host Write-Host "Uploading file to Azure Storage..." -f Yellow $sasUri = $file.azureStorageUri; $global:sasUriClean = [uri]$sasUri $global:sasUriClean = "https://"+$global:sasUriClean.Host+$global:sasUriClean.AbsolutePath UploadFileToAzureStorage $file.azureStorageUri "$IntuneWinFile" $fileUri; [System.IO.Path]::GetDirectoryName("$IntuneWinFile") Remove-Item "$IntuneWinFile" -Force Write-Host Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow $commitFileUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files/$fileId/commit"; MakePostRequest $commitFileUri ($global:fileEncryptionInfo | ConvertTo-Json); Write-Host Write-Host "Waiting for the service to process the commit file request..." -ForegroundColor Yellow $file = WaitForFileProcessing $fileUri "CommitFile"; Write-Host Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow $commitAppUri = "mobileApps/$appId"; $commitAppBody = GetAppCommitBody $contentVersionId $LOBType; MakePatchRequest $commitAppUri ($commitAppBody | ConvertTo-Json); return $mobileApp.id } catch { Write-Host ""; Write-Host -ForegroundColor Red "Aborting with exception: $($_.Exception.ToString())"; } } #################################################### Function Test-AuthToken(){ [CmdletBinding()] param( ) # The variable isn't even loaded in the current session if ((Get-Variable -Scope Global | Where-Object {$_.Name -eq "IntuneToken"}).count -lt 1) { Write-Output -InputObject 'it seems that the variable is not even loaded in the current session' Get-AuthToken } Write-Verbose -Message 'The proper variable does already exist' # There is no Authentication Header for Intune if ($null -eq ($Global:IntuneToken.Header)) { Write-Output -InputObject 'it seems that there does not exist a proper authentication header' Get-AuthToken } Write-Verbose -Message 'There is a proper authentication header available' # since the current date is greater than the expiry time of the token, we need to get a new one $TokenExpires = ($Global:IntuneToken.ResultObject.ExpiresOn.DateTime - (Get-Date).ToUniversalTime()).TotalMinutes if ($TokenExpires -le 0) { Write-Output -InputObject "it seem's that your token has expired $TokenExpires minutes ago. Trying to refresh the Token" $Global:IntuneToken.ResultObject = Get-Msaltoken -ForceRefresh -ClientId $Global:IntuneToken.Authentication.ClientID -RedirectUri $Global:IntuneToken.Authentication.RedirectUri $Global:IntuneToken.Header.Authorization = $Global:IntuneToken.ResultObject.AccessToken } Write-Verbose -Message "The authentication token is still valid for $TokenExpires" $Global:authHeader = $Global:IntuneToken.Header $Global:authHeader | Out-Null $Global:authToken = $Global:IntuneToken.Header $Global:authToken | Out-Null } #################################################### #---------------------------------------------------------[Get XML-File]-------------------------------------------------------- function Get-XMLFile() { WriteHost 1 "Get XML-File informations" [xml]$ApplicationXML = Get-Content $SourcePath\Application.xml $Global:XMLprefix = $ApplicationXML.Application.Prefix $Global:XMLvendor = $ApplicationXML.Application.vendor $Global:XMLname = $ApplicationXML.Application.name $Global:XMLRevision = $ApplicationXML.Application.Revision $Global:XMLversion = $ApplicationXML.Application.version $Global:XMLlanguage = $ApplicationXML.Application.language $Global:XMLsoftwaretype = $ApplicationXML.Application.SoftwareType $Global:XMLScriptAuthor = $ApplicationXML.Application.ScriptAuthor WriteHost 2 "Successfully created registry key" if ($UserInstallation){ $Global:SoftwareRegKey = "Computer\HKEY_CURRENT_USER\SOFTWARE\Wagner\SWD\"+$XMLprefix+$XMLvendor+" "+$XMLname+" "+$XMLversion+" "+$XMLlanguage+" "+$XMLRevision }else { $Global:SoftwareRegKey = "Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Wagner\SWD\"+$XMLprefix+$XMLvendor+" "+$XMLname+" "+$XMLversion+" "+$XMLlanguage+" "+$XMLRevision } Write-Host "----------------------" $Global:SoftwareRegKey Write-Host "----------------------" WriteHost 2 "Successfully created AADGroup name" $Global:AADGroupName = "SG_INTUNE_App_"+$XMLvendor+"-"+$XMLname+"-"+$XMLversion+"-"+$XMLlanguage Write-Host "----------------------" $Global:AADGroupName Write-Host "----------------------" } #---------------------------------------------------------[Create IntuneWIN App]-------------------------------------------------------- function New-IntuneWinApp() { WriteHost 1 "Create IntuneWinApp..." New-Item -ItemType Directory -Path "$temp\$XMLname" | Out-Null &"$PSScriptRoot\IntuneWinAppUtil.exe" -c $SourcePath -s Deploy-Application.exe -o "$temp\$XMLname" -Force WriteHost 2 "IntuneWinApp created successfully" } #---------------------------------------------------------[Check AAD Group]-------------------------------------------------------- function Get-AADGroup() { $GroupExists = $false WriteHost 1 ("Check if "+$AADGroupName+" exists...") $GraphAPI = $GraphAPI+'/groups' $SearchAADGroup = Invoke-RestMethod -Method "GET" -Uri $GraphAPI -Headers $authHeader $SearchAADGroupNames = $SearchAADGroup.value.displayName foreach ($SearchAADGroupName in $SearchAADGroupNames) { if ($SearchAADGroupName -eq $AADGroupName) { $GroupExists = $true } } return $GroupExists } #---------------------------------------------------------[Create AAD-Group]-------------------------------------------------------- function Add-AADGroup() { WriteHost 1 ("Trying to create AAD Group "+$AADGroupName+"...") $params = @{ "description"= "Intune Software Distribution Group"; "displayName"= $AADGroupName; "mailEnabled"= $false; "securityEnabled" = $true; "mailNickname" = "Intune"; } $params = ($params|ConvertTo-Json) $GraphAPI = $GraphAPI+"/groups" $AADGroup = Invoke-RestMethod -Method "POST" -Uri $GraphAPI -Body $params -Headers $authHeader WriteHost 2 ("AAD Group "+$AADGroupName+ " created successfully!") $AADGroupid = $AADGroup.id $GraphAPI = "https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appId/assign" $JSON = @" { "mobileAppAssignments": [ { "@odata.type": "#microsoft.graph.mobileAppAssignment", "target": { "@odata.type": "#microsoft.graph.groupAssignmentTarget", "groupId": "$AADGroupid" }, "intent": "required" } ] } "@ WriteHost 1 "Create Group assignment..." $JSON Invoke-RestMethod -Method "POST" -Uri $GraphAPI -Body $JSON -Headers $authHeader WriteHost 2 "Assignment successful" } #---------------------------------------------------------[Add IntuneWin to Azure Table]-------------------------------------------------------- function AddAzureTable { $global:SourceBody = @{ 'RowKey'=($global:appId) 'PartitionKey'="IntuneWin" 'Name'=($Global:XMLname) 'Version'=($Global:XMLversion) 'Language'=($Global:XMLlanguage) 'Customer'=($Global:XMLprefix) 'Vendor'=($Global:XMLvendor) 'Revision'=($Global:XMLRevision) 'Scriptversion'=($Global:XMLscriptversion) 'UploadUser'= $Global:IntuneToken.ResultObject.Account.Username 'SoftwareType'=($Global:XMLsoftwaretype) 'IntuneWinURI'=($global:sasUriClean) 'EncryptionKey'=($global:fileEncryptionInfo.fileEncryptionInfo.encryptionKey) 'InitializationVector'=($global:fileEncryptionInfo.fileEncryptionInfo.initializationVector) } $global:sourceHeader = @{ 'Content-Type' = 'application/json' 'Accept'="*/*" } $StorageURL = "https://widstwaas001.table.core.windows.net/IntuneUploadTable"+($configPS.SASToken) Invoke-RestMethod -Method "POST" -Uri $StorageURL -Body ($global:SourceBody|ConvertTo-Json) -Header $global:sourceHeader | Out-Null } # ################################################################################ # -------------------------------- [Start Script] -------------------------------- # -------------------------------------------------------------------------------- $PSScriptRoot | Out-Null while ( !$SourcePath ) { $SourcePath = Read-Host -Prompt 'Please enter the Source Path' } while ( $SourcePath.EndsWith("\") ) { $SourcePath = $SourcePath -replace '.$' } try { Write-Host -f Magenta "[Pending] -> Check for updates..." $version = (Get-Module -ListAvailable IntuneWinPackage) | Sort-Object Version -Descending | Select-Object Version -First 1 $psgalleryversion = Find-Module -Name IntuneWinPackage | Sort-Object Version -Descending | Select-Object Version -First 1 $stringver = $version | Select-Object @{n='ModuleVersion'; e={$_.Version -as [string]}} $a = $stringver | Select-Object Moduleversion -ExpandProperty Moduleversion $onlinever = $psgalleryversion | Select-Object @{n='OnlineVersion'; e={$_.Version -as [string]}} $b = $onlinever | Select-Object OnlineVersion -ExpandProperty OnlineVersion if ([version]"$a" -ge [version]"$b") { Write-Host -f DarkGreen "`r[Done] -> Check for updates " } else { Write-Host -f Yellow "`r[Warning] -> This PowerShell module is out of date! Please run Update-Module IntuneWinPackage in a PowerShell session with elevated rights." break } } catch { Write-Host -f Red "`r[Error] -> There is an error with the PowerShell Module. Is it even installed? Try 'Install-Module IntuneWinPackage' in a PowerShell session with elevated rights." break } if (!$SourcePath){ $SourcePath = Read-Host "Source Path: " } WriteHost 1 "Checking parameters..." if (![System.IO.File]::Exists("$SourcePath\Application.png")){ WriteHost 3 "No Icon (Application.png)" Read-Host -Prompt "Press any key to continue" exit } $LocalAppdata = $env:LOCALAPPDATA $temp = "$LocalAppdata\temp" Write-Host -f Magenta "[Pending] -> Checking config file" if (!(Test-Path -Path "$LocalAppdata\IntuneWinPackage\config.json")){ New-Item -ItemType Directory -Force -Path "$LocalAppdata\IntuneWinPackage" | Out-Null New-Item -Path "$LocalAppdata\IntuneWinPackage" -Name "config.json" -Value '{"SASToken": ""}' -Force | Out-Null } #Config $configJSON = Get-Content -Path "$LocalAppdata\IntuneWinPackage\config.json" -raw $configPS = ConvertFrom-Json $configJSON if(!$configPS.SASToken){ Write-Host -f Yellow "`r[Warning] -> No SAS Token set! " $configPS.SASToken = Read-Host "SAS Token: " $configJSON = ConvertTo-Json -depth 32 $configPS $configJSON | Out-File "$LocalAppdata\IntuneWinPackage\config.json" } Write-Host -f DarkGreen "`r[Done] -> Checking config file " WriteHost 2 "Parameters OK!" $baseUrl = "https://graph.microsoft.com/beta/deviceAppManagement/" $logRequestUris = $true; $logHeaders = $false; $logContent = $true; $azureStorageUploadChunkSizeInMb = 6l; Test-AuthToken Get-XMLFile New-IntuneWinApp $SourceFile = "$temp\$XMLname\Deploy-Application.intunewin" $DetectionXML = Get-IntuneWinXML "$SourceFile" -fileName "detection.xml" $RegistryRule = New-DetectionRule -Registry -RegistryKeyPath "$Global:SoftwareRegKey" -RegistryValue "Status" -RegistryDetectionType string -check32BitRegOn64System False $DetectionRule = @($RegistryRule) $ReturnCodes = Get-DefaultReturnCodes $ReturnCodes += New-ReturnCode -returnCode 302 -type softReboot $ReturnCodes += New-ReturnCode -returnCode 145 -type hardReboot if ($ServiceUI){ Add-Win32Lob -SourceFile "$SourceFile" -publisher "$Global:XMLvendor" ` -description "Description" -detectionRules $DetectionRule -returnCodes $ReturnCodes ` -installCmdLine "ServiceUI.exe -Process:explorer.exe Deploy-Application.exe -DeploymentType Install" ` -uninstallCmdLine "ServiceUI.exe -Process:explorer.exe Deploy-Application.exe -DeploymentType Uninstall" }else { Add-Win32Lob -SourceFile "$SourceFile" -publisher "$Global:XMLvendor" ` -description "Description" -detectionRules $DetectionRule -returnCodes $ReturnCodes ` -installCmdLine "Deploy-Application.exe -DeploymentType Install" ` -uninstallCmdLine "Deploy-Application.exe -DeploymentType Uninstall" } if ($CreateGroup){ if (Get-AADGroup) { WriteHost 4 "There already exists a group named $AADGroupName. Should a new group be created?[y/n] (default: n)" $Answer = Read-Host if ($Answer -eq "y") { Add-AADGroup } } else { Add-AADGroup } } AddAzureTable Remove-Item -LiteralPath "$temp\$XMLname" -Force -Recurse } |