Functions/New-IntuneWinPackage.psm1

function New-IntuneWinPackage {
    [cmdletbinding()]

    param (
        [String]$SourcePath,
        [String]$User,
        [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 {

        $userUpn = New-Object "System.Net.Mail.MailAddress" -ArgumentList $User
        $tenant = $userUpn.Host
        Write-Host "Checking for AzureAD module..."
        $AadModule = Get-Module -Name "AzureAD" -ListAvailable

        if ($null -eq $AadModule) {
            Write-Host "AzureAD PowerShell module not found, looking for AzureADPreview"
            $AadModule = Get-Module -Name "AzureADPreview" -ListAvailable
        }

        if ($null -eq $AadModule) {
            write-host
            write-host "AzureAD Powershell module not installed..." -f Red
            write-host "Install by running 'Install-Module AzureAD' or 'Install-Module AzureADPreview' from an elevated PowerShell prompt" -f Yellow
            write-host "Script can't continue..." -f Red
            write-host
            exit
        }

        if($AadModule.count -gt 1){
            $Latest_Version = ($AadModule | Select-Object version | Sort-Object)[-1]
            $aadModule = $AadModule | Where-Object { $_.version -eq $Latest_Version.version }
                if($AadModule.count -gt 1){
                $aadModule = $AadModule | Select-Object -Unique
                }

            $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
            $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
        }
        else {
            $adal = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
            $adalforms = Join-Path $AadModule.ModuleBase "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll"
        }

        [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
        [System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null

        $clientId = "d1ddf0e4-d672-4dae-b554-9d5bdfd93547"
        $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
        $resourceAppIdURI = "https://graph.microsoft.com"
        $authority = "https://login.microsoftonline.com/$Tenant"

        try {
            $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
            $platformParameters = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto"
            $userId = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier" -ArgumentList ($User, "OptionalDisplayableId")
            $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$clientId,$redirectUri,$platformParameters,$userId).Result

            if($authResult.AccessToken){
                $global:authHeader = @{
                    'Content-Type'='application/json'
                    'Authorization'="Bearer " + $authResult.AccessToken
                    'ExpiresOn'=$authResult.ExpiresOn
                    }
                return $authHeader
            }
            else {
                Write-Host
                Write-Host "Authorization Access Token is null, please re-run authentication..." -ForegroundColor Red
                Write-Host
                break
            }
        }
        catch {
            write-host $_.Exception.Message -f Red
            write-host $_.Exception.ItemName -f Red
            write-host
            break
        }
    }
    ####################################################
    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{
            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
    )

    $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.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 = "Uploaded: " + $date;
            $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.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 3 "Access Denied! Check your Endpoint Permissions or PIM activation"
                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(){
        if($global:authToken){

            $DateTime = (Get-Date).ToUniversalTime()
            $TokenExpires = ($authToken.ExpiresOn.datetime - $DateTime).Minutes
            if($TokenExpires -le 0){
                write-host "Authentication Token expired" $TokenExpires "minutes ago" -ForegroundColor Yellow
                write-host
                if($null -eq $User -or $User -eq ""){
                    $Global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
                    Write-Host
                }
                $global:authToken = Get-AuthToken -User $User
            }
        }

        else{
            if($null -eq $User -or $User -eq ""){
                $Global:User = Read-Host -Prompt "Please specify your user principal name for Azure Authentication"
                Write-Host
            }
        $global:authToken = Get-AuthToken -User $User
        }
    }
    ####################################################
    #---------------------------------------------------------[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

        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..."
        mkdir "$temp/$XMLname"
        &"$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+ " createt 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'=$User
            'IntuneWinURI'=($global:sasUriClean)
            'EncryptionKey'=($global:fileEncryptionInfo.fileEncryptionInfo.encryptionKey)
            'InitializationVector'=($global:fileEncryptionInfo.fileEncryptionInfo.initializationVector)
            }

        $global:sourceHeader = @{
            'Content-Type' = 'application/json'
            'Accept'="*/*"
            }

        $StorageURL = "https://psgaintuneuploadtable.table.core.windows.net/uploads"+($configPS.SASToken)
        Invoke-RestMethod -Method "POST" -Uri $StorageURL -Body ($global:SourceBody|ConvertTo-Json) -Header $global:sourceHeader | Out-Null
    }
    #---------------------------------------------------------[Start Script]--------------------------------------------------------
    $PSScriptRoot

    Write-Host -NoNewLine -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
    }

    if (!$SourcePath){
        $SourcePath = Read-Host "Source Path: "
    }
    WriteHost 1 "Checking parameters..."

    if (!$User) {
        WriteHost 3 "No user defined."
        exit
    }
    if (![System.IO.File]::Exists("$SourcePath\Application.png")){
        WriteHost 3 "No Icon (Application.png)"
        exit
    }
    $temp = $env:TEMP
    $LocalAppdata = $env:LOCALAPPDATA
    Write-Host -NoNewLine -f Magenta "[Pending] -> Checking config file"
    if (!(Test-Path -Path "$LocalAppdata/IntuneWinPackage/config.json")){
      New-Item -ItemType Directory -Force -Path "$LocalAppdat/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-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
}