{"id":2741,"date":"2018-09-04T17:53:19","date_gmt":"2018-09-04T15:53:19","guid":{"rendered":"http:\/\/blog.hosebei.ch\/?p=2741"},"modified":"2018-09-04T17:53:19","modified_gmt":"2018-09-04T15:53:19","slug":"create-your-own-software-deployment-repository-with-azure-and-intune","status":"publish","type":"post","link":"https:\/\/blog.hosebei.ch\/?p=2741","title":{"rendered":"Create your own Software Deployment Repository with Azure and Intune"},"content":{"rendered":"<p>As you might know, within Intune you can only install applications on devices, if they are coming as an MSI. If you want to deploy anything else, this blog might be helpful for you.<br \/>\nLet me talk about the requirements:<\/p>\n<li>Azure Subscription for Storage<\/li>\n<li>Intune Subscription (obviously)<\/li>\n<p>This blog will provide the information how you can achieve this, and at the end, you will get a sample implementation from my LAB.<\/p>\n<p>Yes, that&#8217;s it. Now let us start creating the Azure Storage, and how you can access it. Go to the Azure portal, and open the storage accounts section. You can use the classic storage account as well, but I would recommend the newer ones:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02.png?w=268\" alt=\"\" width=\"268\" height=\"300\" class=\"aligncenter size-medium wp-image-2742\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02.png 1216w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02-268x300.png 268w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02-914x1024.png 914w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo02-768x860.png 768w\" sizes=\"auto, (max-width: 268px) 100vw, 268px\" \/><\/a><br \/>\n<!--more--><\/p>\n<p>Click on Add, to add a storage account, if you don&#8217;t want to use an existent one. The creation process of a new storage account is straight forward, mind to select the appropriate replication model:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo03.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo03.png?w=104\" alt=\"\" width=\"104\" height=\"300\" class=\"aligncenter size-medium wp-image-2743\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo03.png 462w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo03-104x300.png 104w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo03-355x1024.png 355w\" sizes=\"auto, (max-width: 104px) 100vw, 104px\" \/><\/a><\/p>\n<p>After the storage Account is created, you can go ahead and get the access token, which we need afterwards. Click on your newly created storage account, and click on &#8220;Shared access signature&#8221; afterwards, the following screen should appear:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04.png?w=300\" alt=\"\" width=\"300\" height=\"167\" class=\"aligncenter size-medium wp-image-2745\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04.png 2017w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04-300x167.png 300w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04-1024x569.png 1024w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04-768x427.png 768w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo04-1536x854.png 1536w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Within this page, you can select how long the generated access token is valid, or for which access it will allow. This is the most restrict version I figured out to use, for downloading the files within the repository.<br \/>\nAllowed Services: Blob<br \/>\nAllowed Resource Types: Service, Container, Object<br \/>\nAllowed Permissions: Read, List<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo01.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo01.png?w=300\" alt=\"\" width=\"300\" height=\"165\" class=\"aligncenter size-medium wp-image-2746\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo01.png 937w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo01-300x165.png 300w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo01-768x421.png 768w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Afterwards you can click on generate SAS and connection string, and copy the SAS token string, thus it&#8217;s the only one we require:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo05.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo05.png?w=300\" alt=\"\" width=\"300\" height=\"154\" class=\"aligncenter size-medium wp-image-2747\" \/><\/a><\/p>\n<p>As said before, copy the &#8220;<strong>SAS token<\/strong>&#8221; string.<\/p>\n<p>Now go ahead to your storage account and add a blog beyond the container option:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06.png?w=269\" alt=\"\" width=\"269\" height=\"300\" class=\"aligncenter size-medium wp-image-2748\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06.png 1092w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06-269x300.png 269w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06-918x1024.png 918w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo06-768x857.png 768w\" sizes=\"auto, (max-width: 269px) 100vw, 269px\" \/><\/a><br \/>\nAfterwards you can simply upload files from your installation package to your container, my example shows the 7-zip Installation:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo07.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo07.png?w=300\" alt=\"\" width=\"300\" height=\"234\" class=\"aligncenter size-medium wp-image-2750\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo07.png 833w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo07-300x234.png 300w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo07-768x600.png 768w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>Now everything is set up, and you can test the download of the file through a browser. For this, you will need those three informations:<\/p>\n<li>URL from Container (will see that shortly)<\/li>\n<li>FileName (defined from you)<\/li>\n<li>SAS Token (as above)<\/li>\n<p>Getting the Container URL is that simple, just click on Properties of your recently created Container, and copy the URL from there:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08.png?w=300\" alt=\"\" width=\"300\" height=\"152\" class=\"aligncenter size-medium wp-image-2751\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08.png 1096w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08-300x152.png 300w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08-1024x519.png 1024w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo08-768x389.png 768w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><br \/>\nNow just put those thing together, in my case:<br \/>\nhttps:\/\/intuneposhrepo01.blob.core.windows.net\/7-zip-1805\/7z1805-x64.exe?$herecomestheSAStoken<\/p>\n<p>Putting this in your browser URL-bar should start the download of the file, and if it&#8217;s working, we only need a script, which will trigger the installation. Here comes my created script in action, it will download the required files, and will start an installation afterwards. The script is not yet golden, it is missing an option to download the files through BITS. And also some Variables inside the Script could be replaced with Input Parameters. Time will tell if I can upgrade the script.<br \/>\nMind to adjust the line 15-17 and 20-21 to your needs. You can also install MSI files, if so, you will need to adjust line 19, beside 20-21.<br \/>\nThe script expects a file called &#8220;fileinfo.txt&#8221; in the root of the Container, conataining all the files required to download on a separate line. The file should lool like follows:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo10.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo10.png\" alt=\"\" width=\"179\" height=\"91\" class=\"aligncenter size-full wp-image-2756\" \/><\/a><\/p>\n<p>And this is the used script:<\/p>\n<p><code>If ($ENV:PROCESSOR_ARCHITEW6432 -eq \"AMD64\") {<br \/>\nTry {<br \/>\n&amp;\"$ENV:WINDIR\\SysNative\\WindowsPowershell\\v1.0\\PowerShell.exe\" -File $PSCOMMANDPATH<br \/>\n}<br \/>\nCatch {<br \/>\nThrow \"Failed to start $PSCOMMANDPATH\"<br \/>\n}<br \/>\nExit<br \/>\n}<\/p>\n<p>function Get-DLFileInfo {<br \/>\n    $webrequest = Invoke-WebRequest -Uri ($ContainerUrl + \"\/fileinfo.txt\" + $SASToken) -UseBasicParsing -ErrorAction Continue<\/p>\n<p>    #return content of file<br \/>\n    return $webrequest.Content<br \/>\n}<\/p>\n<p>function Download-File {<br \/>\n    try {<br \/>\n        $fileDownload = Invoke-WebRequest -Uri $fileDLURL -UseBasicParsing -Method Head -ErrorAction Continue<br \/>\n    }<br \/>\n    catch {<br \/>\n       $returnError = $_.Exception.Message<\/p>\n<p>    }<br \/>\n    finally {<br \/>\n        $fileDownload = Invoke-WebRequest -Uri $fileDLURL -OutFile ($AppTempDLPath + \"\\\" + $file) -UseBasicParsing -ErrorAction SilentlyContinue<br \/>\n    }<br \/>\n    #return status of download<br \/>\n    return $returnError<br \/>\n}<\/p>\n<p>#adjustable Variables (you SHOULD CHANGE THOSE :)<br \/>\n$SASToken = \"?sv=2011-11-09&amp;ss=b&amp;srt=sco&amp;sp=rwl&amp;se=2019-09-18T22:24:51Z&amp;st=2008-09-17T14:24:51Z&amp;spr=https&amp;sig=4pYCjCt7%2B%2B1nCXrHex7NnGJO3242342BybvYR5kSQN521Fk%3D\"  #todo: as input variable<br \/>\n$ContainerUrl = \"https:\/\/intuneposhrepo011111.blob.core.windows.net\/keepass2-40\"     #todo: as input variable<br \/>\n$ClientTempLocation = $env:TEMP<br \/>\n#Application Installation<br \/>\n$MSIInstall = $true # set to true, and use the $installexecutable parameter for your MSI<br \/>\n$InstallExecutable = \"KeePass-2.40.msi\" #if using MSI, name the MSI here. e.g: \"7zipinstall.msi\"<br \/>\n$InstallParameters = \"\/qn\"<\/p>\n<p>###############<br \/>\n##Main Script<br \/>\n#variables<br \/>\n$bContinue = $false<\/p>\n<p>#create temp folder for download, if existent delete it before<br \/>\n#get Application Name from ContainerURL<br \/>\n$ApplicationName = $ContainerUrl.Split(\"\/\")[$ContainerUrl.Split(\"\/\").Count - 1]<br \/>\n#Setup Temp path for Application<br \/>\n$AppTempDLPath = ($ClientTempLocation + \"\\\" + $ApplicationName)<br \/>\n#check for temp folder<br \/>\nif(Test-Path $AppTempDLPath) {<br \/>\n    Remove-Item -Path $AppTempDLPath -Recurse<br \/>\n    $silentcreate = New-Item -Path $AppTempDLPath -ItemType Directory<br \/>\n}<br \/>\nelse {<br \/>\n    $silentcreate = New-Item -Path $AppTempDLPath -ItemType Directory<br \/>\n}<\/p>\n<p>#Get File info for downloading files afterwards<br \/>\n$DLFileContent = Get-DLFileInfo<\/p>\n<p>if($DLFileContent) {<br \/>\n    #download instruction file found, nothing to do here<\/p>\n<p>    #set continue script to true<br \/>\n    $bContinue = $true<br \/>\n} else {<br \/>\n    Write-Warning \"Download File not found\"<\/p>\n<p>    #set continue script to false<br \/>\n    $bContinue = $false<br \/>\n}<\/p>\n<p>#split input file<br \/>\n$DLFileContent = $DLFileContent.Split()<\/p>\n<p>#download the files<br \/>\nif($bContinue -eq $true) {<br \/>\n    $bDLContinue = $true<br \/>\n    #download file was found, now download all the files<br \/>\n    foreach($file in $DLFileContent) {<br \/>\n        #filter empty lines<br \/>\n        if($file) {<br \/>\n            #setup DL URL<br \/>\n            $fileDLURL = $ContainerUrl + \"\/\" + $file + $SASToken<br \/>\n            #download the file<br \/>\n            $DownloadedFile = Download-File<br \/>\n            If($DownloadedFile) {<br \/>\n                #if a file was not correctly downloaded, set variable to false<br \/>\n                $bDLContinue = $false<br \/>\n                Write-Warning (\"Download error: \" + $DownloadedFile)<br \/>\n                Write-Warning (\"The following file could not be downloaded: \" + $file)<\/p>\n<p>            }<br \/>\n            else {<br \/>\n                #if the file was successfully downloaded, notihn to do here<br \/>\n            }<br \/>\n        }<br \/>\n    }<br \/>\n}<\/p>\n<p>#check if script can continue<br \/>\nif($bDLContinue -eq $false) {<br \/>\n    Write-Warning \"A file was not successfully downloaded, Script will not execute further\"<\/p>\n<p>    #set continue script to false<br \/>\n    $bContinue = $false<br \/>\n}<\/p>\n<p>#execute the installation<br \/>\nif($bContinue -eq $true) {<br \/>\n    if($MSIInstall -eq $false) {<br \/>\n        start ($AppTempDLPath + \"\\\" + $InstallExecutable) -ArgumentList $InstallParameters -Wait<br \/>\n    }<br \/>\n    else {<br \/>\n        $msiexec = $env:SystemRoot + \"\\system32\\msiexec.exe\"<br \/>\n        #Write-Host $msiexec -ArgumentList $InstallParameters -Wait<br \/>\n        $PathToMSI = $AppTempDLPath + \"\\\" + $InstallExecutable<br \/>\n        Start-Process $msiexec -ArgumentList (\"\/i \" + $PathToMSI + \" \" + $InstallParameters +\" \/L*vx C:\\windows\\temp\\\" + $ApplicationName + \".log\") -Wait<br \/>\n    }<br \/>\n}<br \/>\n<\/code><\/p>\n<p>After your adjustments, you can test the script on a computer manually, and afterwards you can upload it to Intune. Go to Microsoft Intune -&gt; Device configuration -&gt; PowerShell scripts:<br \/>\n<a href=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09.png?w=300\" alt=\"\" width=\"300\" height=\"133\" class=\"aligncenter size-medium wp-image-2754\" srcset=\"https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09.png 1060w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09-300x133.png 300w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09-1024x454.png 1024w, https:\/\/blog.hosebei.ch\/wp-content\/uploads\/2018\/09\/intune_swrepo09-768x341.png 768w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/a><\/p>\n<p>After adding your script, you can assign it to different users or groups.<\/p>\n<p>Let me know if this was helpful, and if you would like to get a more advanced script, including a Logging Function.<\/p>\n<p>\/Update 19.09.2018: Script adjusted, errors removed, MSI improved<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As you might know, within Intune you can only install applications on devices, if they are coming as an MSI. If you want to deploy anything else, this blog might be helpful for you. Let me talk about the requirements: Azure Subscription for Storage Intune Subscription (obviously) This blog will provide the information how you [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37],"tags":[],"class_list":["post-2741","post","type-post","status-publish","format-standard","hentry","category-software-deployment"],"_links":{"self":[{"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=\/wp\/v2\/posts\/2741","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2741"}],"version-history":[{"count":0,"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=\/wp\/v2\/posts\/2741\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2741"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2741"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.hosebei.ch\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2741"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}