In MMSMOA, Dave, Timmy and me, we presented how to make Flow custom connector for Microsoft Intune. I mentioned I have build an “automation solution” for Autopilot and promised I will write blog post about t.  🙂

Hope you understand, when I came up with idea, it was last year 2018 August. In that time we are still using Windows 1709 and 1803, there wasn’t “AutopilotConfigurationFile.json” file that we can use, and there wasn’t any other option to convert existing Azure AD joined devices to Autopilot, so this “solution” might be already outdated now, but perhaps you can still find something useful in this post. Example make custom connector for Microsoft Intune features.

The idea of making your own connector is not just about Autopilot, you can basically make custom connector for any Microsoft Graph API calls, and then use that with other connectors do any kind of automation actions, and you can use that make Power Apps as well.

How does it work

  1. Use PowerScript get devices serialNumber and hardware hash, use OrderIdentifier for identify different customers’ machine, you can also assign the device to user using assignedUserPrincipalName variable.
  2. After it collects those information, it sends HTTP request to Microsoft Flow.
  3. I have made a flow to revieve this HTTP request, it will use my custom Autopilot connector import device to Autopilot and send results via email.

NOTE

Before you start, you should have already configured all the requirements for Autopilot. You need to create Azure AD dynamic group to collect those imported Autopilot devices, and assigned Autopilot profile to the Azure AD dynamic group. I don’t cover these details in this post.

Create Azure Application

  1. Go to your Azure Portal, Click on Azure Active Directory, click on App registrations, then New registration

  2. Input a name example Flow connector for Intune. Supported account types choose organizational directory only. You can also use any organizational directory,  if you manage multiple tenants and wants use this app to all your tenants.
    Redirect URI, choose Web, and leave value empty

  3. After application is created, in Overview page you will see Application (client) ID, write down this ID, you will need that later for creat flow custom connecotor

  4. Click on Certificates & secrets, then add a New client secret, remember copy the new client secret value to somewhere, you will need that later

  5. Click on API permissions, choose Microsoft Graph, then add Delegated permissions: DeviceManagementServiceConfig.ReadWrite.All and DeviceManagementServiceConfig.Read.All
    And Grand admin consent

 

Create a new Intune Role (RBAC) for Autopilot

In here we are going to create a Intune Role that has minimum permission for import Autopilot devices

  1. Select Intune, click on Roles

  2. Click on All roles, then Add a new role

  3. Create a new role name Flow Autopilot
    Configure permission for import Autopilot device: Enrollment programs , you can adjust these permissions as your own needs

  4. Save and create this new custom role, then open it again from the list. Click on AssignmentsAssign

  5. Input Assignment name, then choose a Group that you will allow member the the group import Autopilot devices (You need to create first a group and add the user as member)

  6. In Scope (Groups), select All Devices

Create custom connector from Postman collection

I have already created all the Autopilot related Graph API calls in Postman collection, download it from my GitHub, if you are interested how to create your own Postman collection, please read this documents https://docs.microsoft.com/en-us/connectors/custom-connectors/create-postman-collection

  1. Go to Flow portal https://flow.microsoft.com/
  2. Click on the setting icon, then choose Custom Connectors

  3. Click on New custom connector, choose Import a Postman collection

  4. Input Connector name anything you like, I put here Autopilot, click on Import, then choose the Windows Autopilot.postman_collection.json that you downloaded from my GitHub, then click on Continue

  5. In General page, you can upload custom icon, choose icon background color, leave other settings as default, click on Security

  6. Choose and input these following settings
    Authentication type: OAuth 2.0
    Identify Provider: Azre Active Directory
    Client id: Your Azure application client ID (the one we just created)
    Client secret: The secret key we genarated in the Azure appliction
    Resource URL: https://graph.microsoft.com

  7. After click on Definition, you should seeing this page. Do any nessesary changes as you wish. In this case, I want to modify CreateImportedwindowsautopilotdeviceidentities, because I want contents of “body” are configured as requried.

  8. After click on body, I configured hardwareIdentifier, orderIdentifier and serialNumber as required

  9. Click on “Back“, and change @odata.type Visibility to internal, so that this property will be hidden from your flow

  10. After finished all the configurations, click on Create connector, then go to Test page
  11. Click on new connection, use a user account that has Import Autopilot permission what what we configured in earlier step Create a new Intune Role (RBAC) for Autopilot

  12. You will properly see this error, telling you that there is no reply address is registered for the application, because we didn’t put any reply address when we created that Azure Application.

  13. Now we go back to the Security page, copy this Redirect URL and paste it back to the Azure application: Authentication – Redirect URL , remember Save the settings. 🙂

  14. If everything goes well (I hope so), you can run some test, example number 7: ListWindowsautopilotdeviceidentities, you should able to see all your imported Autopilot devices.

 

Create a new Flow

Note: If you don’t want skip all these steps, you can import my flow, download from my GitHub here,  import steps can follow this post https://flow.microsoft.com/en-us/blog/import-export-bap-packages/

Here are the manual step how to create this flow

  1. Create a empty flow without any template. Use the first trigger step using “When a HTTP request is received

  2. Copy and paste this JSON code to “Request Body JSON Schema. This is actually the result of the PowerShell script that we will run locally on those machines.
    You properly noticed HTTP POST URL is still empty, but it will generate the url after you save your flow

    {
        "type": "object",
        "properties": {
            "serialNumber": {
                "type": "string"
            },
            "hardwareIdentifier": {
                "type": "string"
            },
            "EmailAddress": {
                "type": "string"
            },
            "assignedUserPrincipalName": {
                "type": "string"
            },
            "orderIdentifier": {
                "type": "string"
            }
        }
    }

  3. Click on Custom, here we will see all our custom connectors. Choose the custom connector that you created for Autopilot

  4. Choose Create importedWindowsAutopilotDeviceIdentities, you should see something like this.

  5. Choose the correct HTTP request value for those properties

  6. Add Parse JSON action, so that we will get correct JSON date that we want.

    {
        "type": "object",
        "properties": {
            "@@odata.context": {
                "type": "string"
            },
            "id": {
                "type": "string"
            },
            "orderIdentifier": {
                "type": "string"
            },
            "serialNumber": {
                "type": "string"
            },
            "productKey": {
                "type": "string"
            },
            "importId": {
                "type": "string"
            },
            "hardwareIdentifier": {
                "type": "string"
            },
            "assignedUserPrincipalName": {
                "type": "string"
            },
            "state": {
                "type": "object",
                "properties": {
                    "deviceImportStatus": {
                        "type": "string"
                    },
                    "deviceRegistrationId": {
                        "type": "string"
                    },
                    "deviceErrorCode": {
                        "type": "integer"
                    },
                    "deviceErrorName": {
                        "type": "string"
                    }
                }
            }
        }
    }

     

  7. Add  Do until condition, because we need to check if the import progress is complete, we want to relatedly check the import status. Don’t input any settings yet.
  8. Inside the Do until, add a Delay action for 15 seconds
  9. Then we add an action to get device import status. Add Get ImportedWindowsAtuopilotDeviceIdentities action from our Autopilot custom connector, choose id from Parse JSON flow

  10. Now we go back to Do until, choose value deviceImportStatus from Get ImportedWindowsAutopilotDeviceIdentities

  11. Configure Do until device import status is not equal to unknown. Count 80, Timeout PT20M.
    This will check device import status every 15 seconds until status is not unknown. It will do that maximum 80 times in 20 minutes.

  12. After this, add Condition, configure if deviceImportStatus is equal to complete, then send an email.

  13. You can also add a parallel branch step, if deviceImportStatus is equal to error, then send an email.

  14. This is how it look in the end

  15. Now click on Save, so that we save our flow, and you will get the unique HTTP Post url. Copy this URL, we will need that for our PowerShell script

PowerShell script

Here is the PowerShell script we use for run locally in those devices, change variable $Url value to the HTTP POST url we just copied.

 

Check the results

After script is run, check your Import Autopilot flow, you should able to see the results.

 

And you should get an email of the result (if you have made the steps to send emails)

 

Go to Intune portal, you will see your device is imported to Autopilot device list.

Now I am off my laptop and start enjoy summer vacation 🙂

 

(2131)

comments
  • Niles Foster
    Posted at 12:37 July 8, 2019
    Niles Foster
    Reply
    Author

    Hi Zeng,

    Can you post a link to how you created the Azure AD dynamic group to collect those imported Autopilot devices, and assigned Autopilot profile to the Azure AD dynamic group?

    Thank you,

    • Zeng Yinghua
      Posted at 13:59 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      Hello Niles, there are many other blog posts has details to how do this, I don’t want to link them all here. If you just make a search using example keyword “autopilot azure ad dynamic group”, I am sure you will find them. Thanks.

  • Niles Foster
    Posted at 18:51 July 8, 2019
    Niles Foster
    Reply
    Author

    Also, is there a way to skip over asking for email, company name, etc? Looking to add this to my script for full AutoPilot automation with new devices.

    • Zeng Yinghua
      Posted at 16:35 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      Hello Niles, yes can skip asking for email and company name, you can delete those parameters from script. If you remove company name variable, remember don’t configure “Create importedWindowsAutopilotDeviceIdentities” orderIdentifier property as required, because it uses the company name variable. If you don’t want make too much changes in the connector or the flow, you can simply modify the PowerShell script, change these variable as not mandatory and predefined values for them. Example:
      Param (
      [Parameter(Mandatory = $false, Position = 1)]
      [string]$YourEmailAddress = “[email protected]”,

      [Parameter(Mandatory = $false, Position = 2)]
      [string]$CompanyName = “KioskPC”,

      [Parameter(Mandatory = $False, Position = 3)]
      [string]$assignedUserPrincipalName
      )

      Regards, Sandy

  • Niles Foster
    Posted at 19:39 July 8, 2019
    Niles Foster
    Reply
    Author

    I am also getting the following error when I try to enter a company name.

    C:\Scripts\autopilotflow.ps1 : Cannot validate argument on parameter ‘CompanyName’. The argument “”COMPANY_NAME”” does not belong to the set “Customer1,Customer2,Customer3”
    specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.
    + CategoryInfo : InvalidData: (:) [autopilotflow.ps1], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,autopilotflow.ps1

    Please advise

    • Zeng Yinghua
      Posted at 14:02 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      The customer1, customer2 and customer3 is just an example for validate if your input is valid, means if you use something else then customer1, customer2 or customer3, it will give you error. You can edit the script by your own needs.

      • Niles Foster
        Posted at 14:50 July 9, 2019
        Niles Foster
        Reply
        Author

        Hi Zeng,

        Ok make sense. However, I get the following error when I select “Customer1”

        C:\Scripts\autopilotflow.ps1 : {“error”:{“code”:”InvalidRequestContent”,”message”:”The request content is not valid and could not be deserialized: ‘Unexpected character encountered
        while parsing value: C. Path ‘orderIdentifier’, line 2, position 20.’.”}}.Exception.Message
        + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
        + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,autopilotflow.ps1

        • Zeng Yinghua
          Posted at 15:33 July 9, 2019
          Zeng Yinghua
          Reply
          Author

          Thank you Niles for trying this. I had typo in my code, line 26. I just fixed the code, can you try again?

  • Rykostars
    Posted at 07:53 July 9, 2019
    Rykostars
    Reply
    Author

    Good article. Graph and intune sure is powerful! I also like how easy it is to use Flow for a scenario like this and being able to easily use it for posting results into Outlook or Teams.

    • Zeng Yinghua
      Posted at 13:58 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      Thanks for your kind comments! You made my day! 😀

      Regards, Sandy

  • Niles Foster
    Posted at 16:23 July 9, 2019
    Niles Foster
    Reply
    Author

    Sorry, same error.

    • Zeng Yinghua
      Posted at 16:50 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      Oh, I copied and pasted it wrong. Try now refresh the blog post and copy the script again.

      • Niles Foster
        Posted at 18:34 July 9, 2019
        Niles Foster
        Reply
        Author

        Ok no errors. 🙂 So how will I know that this worked? I do not see any emails inbound via message trace.

        • Zeng Yinghua
          Posted at 19:11 July 9, 2019
          Zeng Yinghua
          Reply
          Author

          as in the post mentioned, you should able to see how your flow has run, did your flow run and succeed? You can open each step of flow and see the results.

  • Niles Foster
    Posted at 17:08 July 9, 2019
    Niles Foster
    Reply
    Author

    Ok that bypassed them. However, I am still getting the same error from my previous post.

    C:\Scripts\AutoPilot\AutoPilotFlow\autopilotflow.ps1 : {“error”:{“code”:”InvalidRequestContent”,”message”:”The request content is
    not valid and could not be deserialized: ‘Unexpected character encountered while parsing value: C. Path ‘orderIdentifier’, line 2,
    position 20.’.”}}.Exception.Message
    At line:1 char:1
    + .\autopilotflow.ps1
    + ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,autopilotflow.ps1

    • Zeng Yinghua
      Posted at 17:11 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      Did you also copy the new script? Noted from line 20 to 28 are changed.

  • Niles Foster
    Posted at 19:11 July 9, 2019
    Niles Foster
    Reply
    Author

    When I run the script, which is now running without errors, I get the following error in the “Create_importedWindowsAutopilotDeviceIdentities” step in Flow.

    {
    “_version”: 3,
    “Message”: “An error has occurred – Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 – Activity ID: f161b315-291a-4ada-9b53-8d6013ee8ee8 – Url: https://fef.msua06.manage.microsoft.com/DeviceEnrollmentFE/StatelessDeviceEnrollmentFEService/deviceManagement/importedWindowsAutopilotDeviceIdentities?api-version=5019-04-17“,
    “CustomApiErrorPhrase”: “”,
    “RetryAfter”: null,
    “ErrorSourceService”: “”,
    “HttpHeaders”: “{}”
    }

  • Niles Foster
    Posted at 19:21 July 9, 2019
    Niles Foster
    Reply
    Author

    I also noticed under the ” Create importedWindowsAutopilotDeviceIdentities” in the Flow the following. just under “hardwareIdentifier” and above “assignedUserPrincipalName”

    @odata.type

    Not sure how it got there and there does not seem to be a way to remove it.

    • Zeng Yinghua
      Posted at 20:25 July 9, 2019
      Zeng Yinghua
      Reply
      Author

      you can hide the @odata.type by change it’s Visibility to internal in custom connector, the same place where change other properties as required, but that shouldn’t be the issues here.
      What output result did you get from the first step “when a http request received” ? Did it gets the serialNumber, hardwareIdentifier, EmailAddress? If you run test in your Autopilot connector, example the “ListWindowsautopilotdeviceidentities”, did it give you any results or errors?

  • Niles Foster
    Posted at 13:55 July 16, 2019
    Niles Foster
    Reply
    Author

    Hi Zeng,

    Thank you for all your help. The updates to Azure AD account permission was very helpful as well as the script updates. We now have a fully automated solution to import AutoPilot device data.

    Here is the solution that we use with nothing more than the base Windows 10 1903 ISO using NTLite (DISM UI Tool).

    Step #1

    – Download Windows 10 1903 ISO

    – Mount and copy the entire contents to a working directory (e.g. C:\ISO\Windows 10 1903).

    Step #2

    – Create the following PoSH file and save it as AutoPilot.ps1 under C:\ISO\Windows 10 1903\sources\AutoPilot.

    Param (
    [Parameter(Mandatory = $False, Position = 1, HelpMessage = “Please enter your email address”)]
    [ValidateNotNullorEmpty()]
    [string]$YourEmailAddress = “[email protected]”,

    [Parameter(Mandatory = $False, Position = 2, HelpMessage = “Please choose company name:COMPANY NAME”)]
    [ValidateSet(‘COMPANY NAME’)]
    [ValidateNotNullorEmpty()]
    [string]$CompanyName = “COMPANY NAME”,

    [Parameter(Mandatory = $False, Position = 3, HelpMessage = “Please enter assgin users’s UserPrincipalName”)]
    [string]$assignedUserPrincipalName
    )

    $Serial = (Get-WmiObject -Class Win32_BIOS).SerialNumber
    $DeviceHardwareData = (Get-WMIObject -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter “InstanceID=’Ext’ AND ParentID=’./DevDetail'”).DeviceHardwareData

    $body = ConvertTo-JSON @{
    “orderIdentifier” = “$($CompanyName)”
    “serialNumber” = “$($Serial)”
    “hardwareIdentifier” = “$($DeviceHardwareData)”
    “EmailAddress” = “$($YourEmailAddress)”
    “assignedUserPrincipalName” = “$($assignedUserPrincipalName)”
    }

    #Change this to your Flow HTTP request url
    $Url = “https://prod-61.westeurope.logic.azure.com:443/workflows/fdd4d4810dcc46548815d33783544ee0/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%”

    try
    {
    #Make REST API call to flow
    Invoke-RestMethod -uri $Url -Method Post -body $body -ContentType ‘application/json’
    Write-Output “Please wait for email confirmation for approval to continue install this device”
    }
    catch
    {
    Write-Error “$_.Exception.Message”
    }

    TIMEOUT 30

    C:\windows\System32\Sysprep\sysprep.exe /oobe /shutdown

    Step #3

    – Create the following registry file and save it under C:\ISO\Windows 10 1903\sources\AutoPilot\RunOnce.reg.

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce]
    “NextRun”=”C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\Powershell.exe -executionPolicy Unrestricted -File D:\\sources\\AutoPilot\\AutoPilot.ps1”

    Step #4 (NTLite)

    – Add Windows image directory to list.

    – Load only Windows 10 Pro Edition (be patient this takes a while).

    Step #5 (NTLite)

    – Under “Integate> Registry” add the registry file you created in Step #3.

    Step #6 (NTLite)

    – Under “Unattended” set your locale to your requirements. I set mine all to completely automate so that users do not have to select anything with the exception of Wireless Setup. There is a lot here to set so you may need to play around a bit to get all of the settings so as not to prompt during OOBE.

    – Select “Configure disk> Disk template”. Set Type to GPT (UEFI). Note: It is highly recommended not to modify the GPT partitions that Windows creates. What you will see here are the Windows defaults. I ran into issues with factory resetting Windows 10 if these were modified so leave them be.

    – Select “Add local account”, keep the group set to Administrator and check “Auto-Logon this user”

    – Select the “Logon account” and set it to “1”. This will allow the account to login in only once and run the AutoPilot PoSH that you created in Step #2 as defined by the RunOnce in Step #3.

    Step #7 (NTLite)

    – Go to “Apply” and select under “Image format”, “High compression, ready-only (ESD)”. You will need to do this so as not to break the 4GiB file limit for Fat32.

    – Under “Option” check “Create ISO”

    – Click “Process” (be patient this takes a while).

    Step #8

    – Burn the ISO using your favorite tool. I used Rufus.

    That’s it. When you go to image with this ISO and you have Zeng’s Flow in place the device should complete install Windows 10 without any user’s interaction (with the exception of Wireless Setup if not on wired LAN), will import into AutoPilo and send you an email, and when complete will shut down the device automatically, when powered on by the user they will get the OOBE.

  • Niles Foster
    Posted at 13:18 August 23, 2019
    Niles Foster
    Reply
    Author

    Ok, so far this solution has been working great! However, I notice that after a period of time the older flow runs drop off and are no longer accessible thus losing valuable data. Is it possible, since the flow is already collecting the AutoPilot data, to have a CSV generated and attached or sent to a OneDrive separately?

    • Zeng Yinghua
      Posted at 23:55 August 23, 2019
      Zeng Yinghua
      Reply
      Author

      Hello Niles, there are many connectors in Flow, you can use those connectors to build more automation steps. You can also deploy PowerShell script to those machines collect hardware hash and then sent those information to another Flow for example.

  • Leave a Reply

    This site uses Akismet to reduce spam. Learn how your comment data is processed.