Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload MSI / Win32Lob apps #10

Closed
schenardie opened this issue Oct 30, 2018 · 16 comments
Closed

Upload MSI / Win32Lob apps #10

schenardie opened this issue Oct 30, 2018 · 16 comments

Comments

@schenardie
Copy link

Why, is that possible to have more information on how to upload the files (.msi and .intunewin) via this module if supported?? we're trying to automate all creation and deployment via graph API but its hard to find documentation around it.

@rohitramu
Copy link
Contributor

Sorry for the delayed response, it has been a particularly busy month! I am looking into this, and will get back to you soon.

@rohitramu
Copy link
Contributor

I've done some digging, and it looks like the Win32 app type is still in beta - this is why the script provided in the Scenario Modules (mentioned in the docs) does not work with Win32 apps. In order to make that script work with Win32 apps, you would need to:

  1. Make a new version of the UploadLobApp.psm1 module where all the SDK calls have been converted to Invoke-MSGraphRequest calls, so that properties (which are not in v1.0) are correctly serialized/deserialized.
  2. Manually construct the mobileApp object, since the New-MobileAppObject cmdlet won't support app types that are not in v1.0.

Step 1 - Replace all calls to SDK cmdlets with Invoke-MSGraphRequest

For example, in the New-LobApp cmdlet, you would need to convert this call:

# Post the app metadata to Intune
$createdApp = $mobileApp | New-DeviceAppManagement_MobileApps

into this:

# Post the app metadata to Intune
$createdApp = Invoke-MSGraph `
    -HttpMethod POST `
    -Url 'deviceAppManagement/mobileApps' `
    -Content $mobileApp

Essentially, any cmdlet call where the noun starts with DeviceAppManagement would need to be replaced as above. Get calls won't have any content value. Invoke calls would use the POST HttpMethod. Update calls would use the PATCH HttpMethod.

Step 2 - Manually construct the mobileApp object

Also, you would not have a helper method to create the $mobileApp object which is passed into New-LobApp. This means that you will need to construct the mobileApp object yourself. For example, this is some code I might use to upload an iOS LOB app (taken from the UploadIOSLobApp.ps1 sample):

# Create the object that contains information about the app
# !! This is the step that won't be possible for Win32 LOB, since the Win32 LOB app type doesn't exist in v1 !!
$appToUpload = New-MobileAppObject `
    -iosLobApp `
    -displayName 'Test' `
    -description 'This is a test iOS LOB app' `
    -publisher 'Rohit Ramu' `
    -bundleId '' `
    -applicableDeviceType (New-IosDeviceTypeObject -iPad $true -iPhoneAndIPod $true) `
    -minimumSupportedOperatingSystem (New-IosMinimumOperatingSystemObject -v9_0 $true) `
    -fileName 'test.ipa' `
    -buildNumber 'v1' -versionNumber 'v1' -expirationDateTime ((Get-Date).AddDays(90))

# Upload the app file with the app information
# !! Replace 'test.ipa' with the path to your *.ipa file !!
$createdApp = New-LobApp `
    -filePath 'test.ipa' `
    -mobileApp $appToUpload

Write-Output $createdApp

Instead of the using the New-MobileAppObject cmdlet, you would need to construct the object yourself, either in PowerShell as a HashTable or PSCustomObject, or you can create the JSON payload as a string and use ConvertFrom-Json to get it as an object. For example:

# The product code, upgrade code and MSI name can be found in the "Detection.xml" file - open your *.intunewin as a zip file, and browse to IntuneWinPackage\Metadata\Detection.xml
$rawJson = @'
{
    "@odata.type": "#microsoft.graph.win32LobApp",
    "applicableArchitectures": "x64,x86",
    "categories": [],
    "description": "Test",
    "detectionRules": [{
        "@odata.type": "#microsoft.graph.win32LobAppProductCodeDetection",
        "productVersionOperator": "notConfigured",
        "productCode": "{12345678-1234-1234-1234-123456789012}",
        "productVersion": null
    }],
    "developer": "",
    "displayName": "My Win32 LOB App",
    "fileName": "MyWin32LobApp.intunewin",
    "installCommandLine": "msiexec /i \"MyWin32LobApp.msi\" /q",
    "installExperience": {
        "runAsAccount": "system"
    },
    "informationUrl": "",
    "isFeatured": false,
    "largeIcon": null,
    "notes": "",
    "minimumCpuSpeedInMHz": null,
    "minimumFreeDiskSpaceInMB": null,
    "minimumMemoryInMB": null,
    "minimumNumberOfProcessors": null,
    "minimumSupportedOperatingSystem": {
        "v10_1803": true
    },
    "msiInformation": {
        "packageType": "perMachine",
        "productCode": "{12345678-1234-1234-1234-123456789012}",
        "productVersion": "2.20.0.0",
        "requiresReboot": false,
        "upgradeCode": "{87654321-4321-4321-4321-210987654321}"
    },
    "owner": "",
    "privacyInformationUrl": "",
    "publisher": "Rohit Ramu",
    "returnCodes": [{
        "returnCode": 0,
        "type": "success"
    }, {
        "returnCode": 1707,
        "type": "success"
    }, {
        "returnCode": 3010,
        "type": "softReboot"
    }, {
        "returnCode": 1641,
        "type": "hardReboot"
    }, {
        "returnCode": 1618,
        "type": "retry"
    }],
    "runAs32Bit": false,
    "setupFilePath": "MyWin32LobApp.msi",
    "uninstallCommandLine": "msiexec /x \"{12345678-1234-1234-1234-123456789012}\" /q"
}
'@

# Keep in mind that the `productCode` is used in the `detectionRules`, `msiInformation` and `uninstallCommandLine` sections, and that you will need to correctly fill out the `fileName`, `installCommandLine`, `uninstallCommandLine`, `setupFilePath` and `upgradeCode` properties
$appToUpload = ConvertFrom-Json $rawJson

# Upload the app file with the app information
# !! Replace 'MyWin32LobApp.intunewin' with the path to your *.intunewin file !!
$createdApp = New-LobApp `
    -filePath 'MyWin32LobApp.intunewin' `
    -mobileApp $appToUpload

If I get some spare time this week, I will try to put a sample together for you.

@schenardie
Copy link
Author

schenardie commented Nov 29, 2018

Thanks for that Rohi.

Im currently using this
https://github.com/microsoftgraph/powershell-intune-samples/blob/master/LOB_Application/Application_LOB_Add.ps1

I tried ti create this function

function Upload-Win32Lob(){


[cmdletbinding()]

param
(
    [parameter(Mandatory=$true,Position=1)]
    [ValidateNotNullOrEmpty()]
    [string]$IntuneWinLocaton

)

  $list =   Get-ChildItem $IntuneWinLocaton -Filter *.intunewin


  foreach ($item in $list)
 
  {

	try	{
        
                   
        $LOBType = "microsoft.graph.win32LobApp"


        if(Test-SourceFile ($IntuneWinLocaton + "\" + $item.json)){

        Write-Host "JSON file doesn't exist..." -ForegroundColor Red
        Write-Host "Script can't continue..." -ForegroundColor Red
        Write-Host
        break}

        #Test-SourceFile ($IntuneWinLocaton + "\" + $item.json)

        $Win32Path = ($IntuneWinLocaton + "\" + $item.name) 

        # Creating temp file name from Source File path
        $tempFile = [System.IO.Path]::GetDirectoryName("$Win32Path") + "\" + [System.IO.Path]::GetFileNameWithoutExtension("$Win32Path") + "_temp.bin"
        $json = [System.IO.Path]::GetDirectoryName("$Win32Path") + "\" + [System.IO.Path]::GetFileNameWithoutExtension("$Win32Path") + ".json"
        Write-Host
        Write-Host "Creating JSON data to pass to the service..." -ForegroundColor Yellow

        $FileName = [System.IO.Path]::GetFileName("$Win32Path")

        
        $JSON_Data = Get-Content $json
        $JSON_Convert = $JSON_Data | ConvertFrom-Json 
        $JSON_Output = $JSON_Convert | ConvertTo-Json

        
  
        Write-Host
        Write-Host "Creating application in Intune..." -ForegroundColor Yellow
		$mobileApp = MakePostRequest "mobileApps" $JSON_Output;

		# Get the content version for the new app (this will always be 1 until the new app is committed).
        Write-Host
        Write-Host "Creating Content Version in the service for the application..." -ForegroundColor Yellow
		$appId = $mobileApp.id;
		$contentVersionUri = "mobileApps/$appId/$LOBType/contentVersions";
		$contentVersion = MakePostRequest $contentVersionUri "{}";

        # Encrypt file and Get File Information
        Write-Host
        Write-Host "Ecrypting the file '$Win32Path '..." -ForegroundColor Yellow
        $encryptionInfo = EncryptFile $Win32Path  $tempFile;
        $Size = (Get-Item "$Win32Path").Length
        $EncrySize = (Get-Item "$tempFile").Length

		# Create a new file for the app.
        Write-Host
        Write-Host "Creating a new file entry in Azure for the upload..." -ForegroundColor Yellow
		$contentVersionId = $contentVersion.id;
		$fileBody = GetWin32FileBody "$FileName" $Size $EncrySize;
		$filesUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files";
		$file = MakePostRequest $filesUri ($fileBody | ConvertTo-Json);
	
		# Wait for the service to process the new file request.
        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";

		# Upload the content to Azure Storage.
        Write-Host
        Write-Host "Uploading file to Azure Storage..." -f Yellow
		$sasUri = $file.azureStorageUri;
		UploadFileToAzureStorage $file.azureStorageUri $tempFile;

		# Commit the file.
        Write-Host
        Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow
		$commitFileUri = "mobileApps/$appId/$LOBType/contentVersions/$contentVersionId/files/$fileId/commit";
		MakePostRequest $commitFileUri ($encryptionInfo | ConvertTo-Json);

		# Wait for the service to process the commit file request.
        Write-Host
        Write-Host "Waiting for the service to process the commit file request..." -ForegroundColor Yellow
		$file = WaitForFileProcessing $fileUri "CommitFile";

		# Commit the app.
        Write-Host
        Write-Host "Committing the file into Azure Storage..." -ForegroundColor Yellow
		$commitAppUri = "mobileApps/$appId";
		$commitAppBody = GetAppCommitBody $contentVersionId $LOBType;
		MakePatchRequest $commitAppUri ($commitAppBody | ConvertTo-Json);

        Write-Host "Removing Temporary file '$tempFile'..." -f Gray
        Remove-Item -Path "$tempFile" -Force
        Write-Host

        Write-Host "Sleeping for $sleep seconds to allow patch completion..." -f Magenta
        Start-Sleep $sleep
        Write-Host

	}
	
    catch {

		Write-Host "";
		Write-Host -ForegroundColor Red "Aborting with exception: $($_.Exception.ToString())";
	
    }

  }

}

It does create upload inserts the json with all the correct functions but never installs. Im not sure which component is failing but if i re-use the same app and re-upload the .intunewin everything works. i think once is compressing and creating the .bin file something that does work for when using MSI's doesnt when using intunewin files.

@rohitramu
Copy link
Contributor

I actually learned recently that uploading a win32 app works a little bit differently to other app types. Given this, I think you're doing the right thing by using the samples.

Since this is off-topic for this repository, I would suggest opening an issue in the Intune PowerShell Samples GitHub repository that you downloaded this script from: https://github.com/microsoftgraph/powershell-intune-samples/issues.

I will close this issue for now, but please feel free to re-open it (or create a new issue) if you have more questions about the Intune PowerShell SDK.

@schenardie
Copy link
Author

Well its not, to be honest i'd expect that to be part of the Intune official SDK since its an option (eve thoug still in preview) to do it via console i think it should be supported via graph api as well.

Thanks

@rohitramu
Copy link
Contributor

rohitramu commented Dec 10, 2018

Sorry, let me clarify: the Intune PowerShell Sample scripts (found at https://github.com/microsoftgraph/powershell-intune-samples) is off topic for this repository. Please submit issues about the samples in that repo.

Win32 LOB apps are an important feature in Intune. Currently, it is in the beta schema because it is a new feature. Once we are confident in the stability of this new API, we will promote it to v1.0.

The PowerShell SDK is built on the v1.0 schema specifically for this reason - APIs in beta can change, and so building the SDK on the beta schema introduces the possibility of changes which can break the scripts that use it. Once the Win32 LOB app feature is available in the v1.0 schema, we will be able to provide it in the Intune PowerShell SDK as well.

In terms of the console, our UI in the Azure Portal supports Win32 LOB apps because it makes calls to the beta schema. We do this because it allows us to iterate much faster to bring features to the UI. Regarding changes to the beta schema, we are able to prevent breaks in the UI as it is developed in parallel with our Graph APIs.

@aaronparker
Copy link

aaronparker commented Apr 20, 2019

Any script that we would build using the SDK would be far simpler than going with this method: Application_LOB_Add.ps1. I'd rather you break simple scripts using the API, than complex scripts such as those in the examples.

Given how fast Intune is changing, it's not practical to wait for the SDK to only support v1.0 APIs. Win32 LoB apps were introduced in beta in October 2018, it's now April 2019.

Please reconsider the stance for APIs that aren't v1.0 yet. At the very least, please support calling a beta API with Invoke-MSGraphRequest.

@Crucerio
Copy link

Crucerio commented Apr 28, 2019

So what now? Do we have to reverse-engineer the javascript on Azure Portal to get the special encoding/encryption? Please get someone to transcode it to PowerShell. I get as far as some others here... but the Azure Blob Upload seams to really need some different digested file for win32Apps.

@tjgruber
Copy link

tjgruber commented Jul 30, 2019

I simply want to replace the .intunewin file of an existing app programmatically with Graph API in PowerShell instead of doing it manually through the Intune portal, even if I need to use the beta API. This is very urgent. Is it possible?

I have the .intunewin file ready to go, I just need to replace an existing Intune App's .intunewin file with this new one.

@Crucerio
Copy link

hi tjgruber, you will have to use the msgraph api to query for an azure storage blob id, try it in the msgraph explorer. The Blob storage id will allow you to place the new intunewin container in azure, which has to be queried through the azure storage api. The example how to upload those files is in the win32 Lob powershell script. You will have to provide the id and the new file. If the id does not work, see what web-calls the azure portal ui issues and do the same with powershell invoke-webrequest. Can,t be more specific yet, but soon, when I will be facing a similar task, I will report back here, if still needed.(in around september i think)

@tjgruber
Copy link

tjgruber commented Jul 31, 2019

hi tjgruber, you will have to use the msgraph api to query for an azure storage blob id, try it in the msgraph explorer. The Blob storage id will allow you to place the new intunewin container in azure, which has to be queried through the azure storage api. The example how to upload those files is in the win32 Lob powershell script. You will have to provide the id and the new file. If the id does not work, see what web-calls the azure portal ui issues and do the same with powershell invoke-webrequest. Can,t be more specific yet, but soon, when I will be facing a similar task, I will report back here, if still needed.(in around september i think)

Hi Crucerio, I proceeded with your suggestion and was able to successfully upload and commit a new intunewin file to the win32lobapp. However, the app continues to use the old "committedContentVersion". When I go into the app in the Intune web GUI and manually upload a new intunewin file, the app automatically uses the package. But again, when I upload and commit the intunewin file through the API using some functions from Win32_Application_Add.ps1, the App does not use the newly uploaded intunewin file, it continues to use the old version. I confirm this when I look at the following URI and setting: "committedContentVersion": "#" (where # is the old version prior to my api upload)

https://graph.microsoft.com/beta/deviceAppManagement/mobileApps/$appID

When I try to change the committedContentVersion manually in Graph Explorer to use the intunewin file I uploaded via API, It gives an error saying: "Invalid operation: app's PublishingState is not 'Published'.

So in summary, your suggestion worked and I was able to successfully upload and commit my intunewin file programmatically, but the problem now is that the app that I'm updating with this new file does not use the new file. It still deploys the old intunewin file, unless I upload a new one manually via the web GUI. So, how is the web gui telling the app to use the newly uploaded intunewin file? That's what I want to do now.

@Crucerio
Copy link

Crucerio commented Jul 31, 2019

Hi again tjgruber, the progress you already achieved sounds very close to the solution. The last bit will be found in the graph and storage api calls, when uploading a new container. Start logging the webrequests just before choosing a new file to upload in the azure portal. Meanwhile repeatedly query the app in msgraph to track down the time, when the actual app revision changes. That will give you the hint what payload has to be injected to the win32 app in msgraph. Maybe it just needs a reference to the completed upload, to be sure it‘s upload is complete. Hope this helps you track it down. If you succeed, some others here would sure be pleased to get that hint, too, me included.

@tjgruber
Copy link

tjgruber commented Aug 5, 2019

Hi again tjgruber, the progress you already achieved sounds very close to the solution. The last bit will be found in the graph and storage api calls, when uploading a new container. Start logging the webrequests just before choosing a new file to upload in the azure portal. Meanwhile repeatedly query the app in msgraph to track down the time, when the actual app revision changes. That will give you the hint what payload has to be injected to the win32 app in msgraph. Maybe it just needs a reference to the completed upload, to be sure it‘s upload is complete. Hope this helps you track it down. If you succeed, some others here would sure be pleased to get that hint, too, me included.

I couldn't figure it out. So I ended up altering that script to work in an Azure DevOps pipeline to make a new app and delete the old one for every update. There's some more testing to be done regarding how that will effect existing installations, but until there's an easier way to replace or update existing intunewin files programmatically or through graph api, that's what I have to do to move on.

@schenardie
Copy link
Author

@schenardie
Copy link
Author

@tjgruber
Copy link

tjgruber commented Aug 5, 2019

https://github.com/microsoftgraph/powershell-intune-samples/tree/master/LOB_Application

Yes you must have missed my earlier comments. I tried those and even though they successfully upload and commit the intunewin file, the InTune app does not use it, as it keeps using the previous version of the package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants