Giter VIP home page Giter VIP logo

intunebackupandrestore's People

Contributors

jseerden avatar saltypeaches avatar sleeuwenhoek avatar ztrhgf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

intunebackupandrestore's Issues

400 Bad Request importing iOS Device Restrictions

When trying to import iOS Device Restrictions I get a 400 Bad Request error (error message below).
Work around is to open the file and remove the following part of the document located in "networkUsageRules" at the end of the document:

                          {
                              "cellularDataBlockWhenRoaming":  false,
                              "cellularDataBlocked":  false,
                              "managedApps":  ""
                          },
                          {
                              "cellularDataBlockWhenRoaming":  false,
                              "cellularDataBlocked":  false,
                              "managedApps":  ""
                          }

The error we get is as following:

Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request
{
"error": {
"code": "BadRequest",
"message": "Property managedApps in payload has a value that does not match schema.",
"innerError": {
"date": "2021-02-15T13:16:04",
"request-id": "6961121f-044d-4ef7-bbc8-414a5af8ab67",
"client-request-id": "6961121f-044d-4ef7-bbc8-414a5af8ab67"
}
}
}
At C:\Program Files\WindowsPowerShell\Modules\IntuneBackupAndRestore\2.0.0\Public\Start-IntuneRestoreConfig.ps1:28
char:5

  • Invoke-IntuneRestoreDeviceConfiguration -Path $Path
    
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    • FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfiguratio
      n

PowerShell script encoding changes

I uploaded PowerShell scripts with UTF-8 encoding and they're working as expected. When I run IntuneBackupAndRestore, the PS1 file in the Script Content folder is encoded as UCS-2 LE BOM. Restoring these scripts appears to work, but they result in errors when pushed to the clients due to the encoding.

400 bad request restoring EDR settings

I backed up entire Intune setup from one tenant, then individually restored specific groups of settings to another tenant I manage. Although a lot of settings restored fine, I had some errors I didn't understand. This is the second:

PS C:\Users\ichil> Invoke-IntuneRestoreDeviceManagementIntent -Path c:\stuff\intunebackup

VERBOSE: Default EDR policy for all devices - Failed to restore Device Management Intent (Endpoint detection and response)
Invoke-IntuneRestoreDeviceManagementIntent : 400 Bad Request
{"error":{"code":"BadRequest","message":"{\r\n "_version": 3,\r\n "Message": "An error has occurred - Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 -
Activity ID: 153136c9-699a-4dd7-ab1a-63c73ba00d47 - Url: https://fef.msud01.manage.microsoft.com/DeviceManagementIntent/DeviceManagementIntentService/83661860-ffff-2121-0449-060208542767/devic
eManagement/templates%28%27e44c2ca3-2f9a-400a-a113-6cc88efd773d%27%29/microsoft.management.services.api.createInstance?api-version=5020-08-21",\r\n "CustomApiErrorPhrase": "",\r\n
"RetryAfter": null,\r\n "ErrorSourceService": "",\r\n "HttpHeaders":
"{}"\r\n}","innerError":{"date":"2022-06-06T02:37:51","request-id":"153136c9-699a-4dd7-ab1a-63c73ba00d47","client-request-id":"153136c9-699a-4dd7-ab1a-63c73ba00d47"}}}
At line:1 char:2

  • Invoke-IntuneRestoreDeviceManagementIntent -Path c:\stuff\intuneback ...
  •  + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
     + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceManagementIntent
    
    
    

EDR JSON

{
"roleScopeTagIds": [
"0"
],
"settingsDelta": [
{
"@odata.type": "#microsoft.graph.deviceManagementStringSettingInstance",
"id": "40445d40-9dca-495b-a33d-9e4cda659a62",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionBlobType",
"valueJson": ""notConfigured"",
"value": "notConfigured"
},
{
"@odata.type": "#microsoft.graph.deviceManagementBooleanSettingInstance",
"id": "71cd7124-0693-4b3e-8260-8d5461f92358",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionAutoPopulateOnboardingBlob",
"valueJson": "true",
"value": true
},
{
"@odata.type": "#microsoft.graph.deviceManagementStringSettingInstance",
"id": "d7521428-ef6a-4c32-8cc6-4d74d1f1e35c",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOffboardingBlob",
"valueJson": "null",
"value": null
},
{
"@odata.type": "#microsoft.graph.deviceManagementStringSettingInstance",
"id": "a086ef45-ff58-4b87-8fd4-a3e4cdfece5b",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOffboardingFilename",
"valueJson": "null",
"value": null
},
{
"@odata.type": "#microsoft.graph.deviceManagementStringSettingInstance",
"id": "b865e289-01a6-452a-bb5d-947558d53d59",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOnboardingBlob",
"valueJson": ""Value has been set"",
"value": "Value has been set"
},
{
"@odata.type": "#microsoft.graph.deviceManagementStringSettingInstance",
"id": "a5f5539f-c6be-4f94-968d-c9fded7bf8f0",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_advancedThreatProtectionOnboardingFilename",
"valueJson": "null",
"value": null
},
{
"@odata.type": "#microsoft.graph.deviceManagementBooleanSettingInstance",
"id": "55b5588f-127c-43d0-b2df-5deb0395bc2e",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_allowSampleSharing",
"valueJson": "true",
"value": true
},
{
"@odata.type": "#microsoft.graph.deviceManagementBooleanSettingInstance",
"id": "e6a69961-0bc9-468f-97d1-87d9ba87dc32",
"definitionId": "deviceConfiguration--windowsDefenderAdvancedThreatProtectionConfiguration_enableExpeditedTelemetryReporting",
"valueJson": "true",
"value": true
}
],
"description": "Default EDR policy for targetting all tenants devices, created by MDE.",
"displayName": "Default EDR policy for all devices"
}

I'm wondering whether this may be a consequence of having my intune backup and PS1 directories in a deeply nested folder hierarchy with long folder names? For example:
"C:\Users\ichil\OneDrive\Documents\WindowsPowerShell\Modules\IntuneBackupAndRestore\3.2.0\Public\Invoke-IntuneRestoreDeviceConfigurationAssignment.ps1"
"C:\Stuff\IntuneBackup\Settings Catalog\Assignments\PBC Activate tamper protection.json"

Compare-IntuneBackupDirectories handling for new (added) files in DifferenceDirectory

Comparing two full backup directories while the more recent one has some items added (for example a new Intune Configuration profile has been created since the last backup) does not provide any output for the user. This may mislead user to believe nothing has changed.

Reproduction steps:

  1. Create full backup via Start-IntuneBackup (this will be the ReferenceDirectory)
  2. Make a change in Intune which would reflect as an added (new) JSON file in the next backup (create a new device configuration profile for example)
  3. Create another full backup via Start-IntuneBackup (this will be the DifferenceDirectory)
  4. Run a compare via Compare-IntuneBackupDirectories with the above two backup directories

Proposed solution:
Referencing file "\Public\Compare-IntuneBackupDirectories.ps1"

  • After looping through all files in the $referenceFiles variable, additionally check that no items which exist in the $differenceFiles variable were not present.
  • If new files are detected, notify user in the console
    • E.g. Write-Host "ADDED FILE: Difference directory file '[FilePath]' does not exist in the reference directory."

Invoke-IntuneRestoreDeviceConfiguration Error

Hi, there is an error when trying to restore an iOS configuration profile with Invoke-IntuneRestoreDeviceConfiguration cmdlet

`ConvertFrom-Json : Invalid JSON primitive: .
At C:\Program Files\WindowsPowerShell\Modules\IntuneBackupAndRestore\2.0.0\Public\Invoke-IntuneRestoreDeviceConfiguration.ps1:37 char:74
+ ... DisplayName = ($deviceConfigurationContent | ConvertFrom-Json).displa ...
+                                                  ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand

ConvertFrom-Json : Invalid JSON primitive: .
At C:\Program Files\WindowsPowerShell\Modules\IntuneBackupAndRestore\2.0.0\Public\Invoke-IntuneRestoreDeviceConfiguration.ps1:40 char:60
+ ...   $requestBodyObject = $deviceConfigurationContent | ConvertFrom-Json
+                                                          ~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [ConvertFrom-Json], ArgumentException
    + FullyQualifiedErrorId : System.ArgumentException,Microsoft.PowerShell.Commands.ConvertFromJsonCommand`

It is a iOS Device configuration Profile with the Profile Type "Device restrictions".
It contains the following configuration.

image

Thank you

Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request while restoring endpoint protection policy

While running Start-IntuneRestoreConfig ".\test" (which contains a Device Confirguration folder and a single .json file), I see this message and the policy will fail to create.

Windows 10 - Endpoint protection - Failed to restore Device Configuration
Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request
{
  "error": {
    "code": "ModelValidationFailure",
    "message": "An unexpected 'PrimitiveValue' node was found when reading from the JSON reader. A 'StartArray' node was expected. Error found near: :false},\"@odata.type\":\"#microsoft.management.services.api.windows10EndpointProtectionConfiguration\"} 
<---",
    "innerError": {
      "message": "An unexpected 'PrimitiveValue' node was found when reading from the JSON reader. A 'StartArray' node was expected. Error found near: :false},\"@odata.type\":\"#microsoft.management.services.api.windows10EndpointProtectionConfiguration\"} 
<---",
      "date": "2021-02-15T23:18:40",
      "request-id": "5d9726d7-3715-4905-90ce-436a72271890",
      "client-request-id": "5d9726d7-3715-4905-90ce-436a72271890"
    }
  }
}
At C:\Program Files\WindowsPowerShell\Modules\intunebackupandrestore\Public\Start-IntuneRestoreConfig.ps1:28 char:5
+     Invoke-IntuneRestoreDeviceConfiguration -Path $Path
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfiguration

Adding MISSING Items

I love this tool as a backup/restore or even import process for tenant to tenant migrations. Thank you so much for making IT admins and consultants our life easier.

it would be convenient if it can include the following:
Enrollment Policies
Conditional Access Policies
App Protection Policies
App Configuration Policies
Software Update Rings (nvm, found them under Device Configuration folder!!!)
Autopilot Profile Policies
Corporate Device Identifiers

Allow multiple options to restore based on the workload (ex. Policy for devices, apps, conditional, autopilot, app config, device config, enrollment, etc..)

Again, amazing work and thank you for considering adding these new features within the module.

IntuneBackupAndRestore v3.2.0 no Proactive Remediations scripts backuped

First thanks for the great script!
I have upgraded to the latest version 3.2.0 but when I do a full backup I don't see any of the Proactive Remediations scripts in the backup folder.
Get-installedmodule is showing the following: 3.2.0 IntuneBackupAndRestore PSGallery
We have 13 remediations scripts some only detection and some detection and remediation, none are backuped.

Regards Menno

Update to include missing items

Hi. There are a number of items not included. I am getting around this by calling your script plus some additions, see below. Would you be able to incorporate / improve? Note: I switched from .json to .XML has I keep finding scenarios in M365 where there are duplicate properties ("Id","ID"), which was getting in the way of a comparison function.

Function backup_data
{
If((test-path -Path ($backup_path + '' + $aspect)) -eq $false){md ($backup_path + '' + $aspect)}
Foreach($data in $dataset)
{
$name = $null
If($data.DisplayName -ne $null){$name = $data.DisplayName.ToString()}
Elseif($data.name -ne $null){$name = $data.name.ToString()}
Elseif($data.ID -ne $null){$name = $data.ID.ToString()}
Elseif($data.id -ne $null){$name = $data.ID.ToString()}
Else{$name = $data.Identity.ToString()}

    Foreach($chr in $invalidcharacters)
    {
        $name = $name.replace($chr,"")
    }
    $name = $name.TrimStart(" ")
    #$data | ConvertTo-Json -depth 100 | set-content ($backup_path + '\' + $aspect + '\' + $name + '.json') -Force
    $data | Export-Clixml -Depth 100 -LiteralPath ($backup_path + '\' + $aspect + '\' + $name + '.xml') -Force
}

}

$backup_path = $pathroot + '' + $year + '' + $date + '\Intune'
$TasksCount++
write-progress -Activity "Backing up M365 Tenant Configuration" -status "Intune" -PercentComplete ($TasksCount/$Tasks*100)

Start-IntuneBackup -Path $backup_path

#Device Categories
$aspect = "Device Category"
$dataset=$null;$dataset=Get-IntuneDeviceCategory
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

#App Protection policies
#Intune App protection - Android
$dataset=$null;$dataset = Get-MgDeviceAppManagementAndroidManagedAppProtection
$aspect="App Protection - Android"
write-host "backing up:" $aspect;
backup_data
    # Assigned apps
    If((test-path -Path ($backup_path + '\' + $aspect+ '\Apps')) -eq $false){md ($backup_path + '\' + $aspect+ '\Apps')}
    Foreach($data in $dataset)
    {
        $name = $null
        $name = ($data.DisplayName.ToString() + '_APPS')
        Foreach($chr in $invalidcharacters)
        {
            $name = $name.replace($chr,"")
        }
        #Get-MgDeviceAppManagementAndroidManagedAppProtectionApp -AndroidManagedAppProtectionId $data.Id  | ConvertTo-Json -depth 100 | set-content ($backup_path + '\' + $aspect + '\' + '\Apps\' + $name + '.json') -Force
        Get-MgDeviceAppManagementAndroidManagedAppProtectionApp -AndroidManagedAppProtectionId $data.Id  | Export-Clixml -Depth 100 -LiteralPath ($backup_path + '\' + $aspect + '\' + '\Apps\' + $name + '.xml') -Force
    }


                       
#Intune App protection - iOS
$dataset =$null
$dataset=$null;$dataset = Get-MgDeviceAppManagementiosManagedAppProtection
$aspect="App Protection - iOS"
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

# Assigned apps
If((test-path -Path ($backup_path + '\' + $aspect+ '\Apps')) -eq $false){md ($backup_path + '\' + $aspect+ '\Apps')}
Foreach($data in $dataset)
{
    $name = $null
    $name = ($data.DisplayName.ToString() + '_APPS')
    Foreach($chr in $invalidcharacters)
    {
        $name = $name.replace($chr,"")
    }
    Get-MgDeviceAppManagementiOSManagedAppProtectionApp -iOSManagedAppProtectionId $data.Id  | Export-Clixml -Depth 100 -LiteralPath ($backup_path + '\' + $aspect + '\' + '\Apps\' + $name + '.xml') -Force
     
}
$aspect="Intune\App Protection - iOS\Apps"



#Device Enrollment Configuration
$aspect = "Device Enrollment Configuration"
$dataset=$null;$dataset=get-IntuneDeviceEnrollmentConfiguration
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

$aspect = "Device Enrollment Configuration\Assignments"
If((test-path -path ($backup_path + '\' + $aspect)) -eq $false){md ($backup_path + '\' + $aspect)}
Foreach($data in $dataset)
{
    $name=$null
    $name = $data.displayName
    Foreach($chr in $invalidcharacters){$name = $name.replace($chr,"")}
    Get-IntuneDeviceEnrollmentConfigurationAssignment -deviceEnrollmentConfigurationId $data.deviceEnrollmentConfigurationId |  Export-Clixml -Depth 100 -LiteralPath ($backup_path + '\' + $aspect +'\' + $name + '.xml')
}


#ApplePushNotificationCertificate
$aspect = "Certificates"
$cert = Get-IntuneApplePushNotificationCertificate
If($cert -ne $null)
{
    md ($backup_path + '\' + $aspect)
    #$cert | ConvertTo-Json -Depth 100 | Set-Content ($backup_path + '\' + $aspect +'\ApplePushNotificationCertificate.json')
    $cert | Export-Clixml ($backup_path + '\' + $aspect +'\ApplePushNotificationCertificate.xml')
}

#Get-IntuneTermsAndConditions
$aspect = "Terms And Conditions"
$dataset=$null;$dataset=Get-IntuneTermsAndConditions
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

$aspect = "Terms And Conditions\Assignments"
If((test-path -path ($backup_path + '\' + $aspect)) -eq $false){md ($backup_path + '\' + $aspect)}
Foreach($data in $dataset)
{
    $name=$null
    $name = $data.displayName
    Foreach($chr in $invalidcharacters){$name = $name.replace($chr,"")}
    #Get-IntuneTermsAndConditionsAssignment -termsAndConditionId $data.Id | ConvertTo-Json -Depth 100 | Set-Content ($backup_path + '\' + $aspect +'\' + $name + '.json')
    Get-IntuneTermsAndConditionsAssignment -termsAndConditionId $data.Id | Export-Clixml -Depth 100  -LiteralPath ($backup_path + '\' + $aspect +'\' + $name + '.xml')
}


#Get-IntuneMdmWindowsInformationProtectionPolicy
$aspect = "Windows Information Protection"
$dataset=$null;$dataset=Get-IntuneMdmWindowsInformationProtectionPolicy
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

#MicrosoftStoreForBusiness

$aspect="MicrosoftStoreForBusiness"
$dataset=$null;$dataset = Get-MgDeviceAppMgt
If($dataset -ne $null)
{
    md ($backup_path + '\' + $aspect)
    $dataset | Export-Clixml ($backup_path + '\' + $aspect +'\DeviceAppMgt.xml')
}

#Intune Admin Roles
$dataset =$null;$dataset = Get-IntuneRoleDefinition
$aspect="Admin Roles"
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

#Intune Admin Role assignments
$aspect="Admin Roles\Assignments"
$dataset =$null;$dataset = Get-IntuneRoleAssignment
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}



$aspect="DeviceManagementPartner"
$dataset =$null;$dataset = Get-IntuneDeviceManagementPartner
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}


$aspect="ExchangeConnector"
$dataset =$null;$dataset = Get-IntuneExchangeConnector
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

$aspect="MobileThreatDefenseConnector"
$dataset =$null;$dataset = Get-IntuneMobileThreatDefenseConnector
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}


$aspect="NotificationMessageTemplate"
$dataset =$null;$dataset = Get-IntuneNotificationMessageTemplate
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}


$aspect="TelecomExpenseManagementPartner"
$dataset =$null;$dataset = Get-IntuneTelecomExpenseManagementPartner
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}


$aspect="RemoteAssistancePartner"
$dataset =$null;$dataset = Get-IntuneRemoteAssistancePartner
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}


$aspect="VppToken "
$dataset =$null;$dataset = Get-IntuneVppToken
If($dataset -ne $null){write-host "backing up:" $aspect;backup_data}

SettingsCatalog - Device configuration profile not backup

Hi John,

First love your script to backup and restore endpoint manager configuration!
Do you have any idea when you will update the PowerShell module so that settings catalog device configuration profiles will be included? Thank you in advance!

Windows 365 Cloud PC settings?

Can you include Windows 365 Cloud PC settings for backup and restore?

Provisioning policies
Azure Network Connection policies
User Settings policies
Custom Images profiles

Thank you for the amazing work!!!

Remove invalid characters from filenames

Backup of security baselines for MMD failing due to invalid characters in the name. in my own script I got around via following, but would much prefer you incorporate into your excellent module:
$invalidcharacters = @('/','$','[',']','{','{','~','#','!','?',':','(',')','}','{','&')
Foreach($chr in $invalidcharacters)
{
$name = $name.replace($chr,"")
}
$name = $name.TrimStart(" ")

Getting all clients apps

Hi Guys.

When i try to make a back-up of the clients apps with Invoke-IntuneBackupClientApp.ps1 i will get all application that are not installed and visible in my MEM environment. I have this issues in 5 tenants. So i think it's something with the script of graph.

These are the standard apps that I'm retrieving. with : $clientApps = Get-DeviceAppManagement_MobileApps | Get-MSGraphAllPages from the Invoke-IntuneBackupClientApp.ps1 script.

Adobe Acrobat Reader
Adobe Acrobat Reader
Adobe Acrobat Reader for Intune (Deprecated)
Adobe Acrobat Reader for Intune (Deprecated)
Azure Information Protection
Azure Information Protection
BlueJeans Video Conferencing
BlueJeans Video Conferencing
Board Papers
Box
Box — Cloud Content Management
Box for EMM
Breezy for Intune
CellTrust SL2™ for Intune
CellTrust SL2™ for Intune
Cisco Jabber for Intune
Cisco Jabber for Intune
Citrix ShareFile for Intune
Citrix ShareFile for Intune
Comfy
Comfy
Cortana
Cortana
Dynamics 365 Remote Assist
Dynamics 365 Remote Assist
FactSet
FactSet
Field Service (Dynamics 365)
Field Service (Dynamics 365)
Field Service Mobile
Field Service Mobile
FleetSafer
FleetSafer
Fuze Mobile for Intune
Fuze Mobile for Intune
Hearsay Relate for Intune
Hearsay Relate for Intune
iBabs For Intune
ISEC7 Mobile Exchange Delegate for Intune
Lexmark Mobile Print Intune
Meetio
Meetio
MentorcliQ
MentorcliQ
M-Files for Intune
M-Files for Intune
Microsoft 365 Admin
Microsoft 365 Admin
Microsoft Bookings
Microsoft Bookings
Microsoft Connections
Microsoft Connections
Microsoft Dynamics 365
Microsoft Dynamics 365 for phones
Microsoft Dynamics 365 for phones
Microsoft Dynamics 365 for tablets
Microsoft Edge
Microsoft Edge
Microsoft Excel
Microsoft Excel
Microsoft Invoicing
Microsoft Invoicing
Microsoft Kaizala
Microsoft Kaizala
Microsoft Launcher
Microsoft Lists
Microsoft OneDrive
Microsoft OneDrive
Microsoft OneNote
Microsoft OneNote
Microsoft Outlook
Microsoft Outlook
Microsoft Planner
Microsoft Planner
Microsoft Power Apps
Microsoft Power BI
Microsoft Power BI
Microsoft PowerPoint
Microsoft PowerPoint
Microsoft SharePoint
Microsoft SharePoint
Microsoft StaffHub
Microsoft StaffHub
Microsoft Stream
Microsoft Stream
Microsoft Teams
Microsoft Teams
Microsoft To-Do
Microsoft To-Do
Microsoft Visio Viewer
Microsoft Whiteboard
Microsoft Word
Microsoft Word
MultiLine for Intune
MultiLine for Intune
Nine Work for Intune
Notate for Intune
Notate for Intune
Now® Mobile - Intune
Now® Mobile - Intune
Omnipresence Go
Omnipresence Go
Outlook Groups
Outlook Groups
Power Apps
Power Automate
Power Automate
PrinterOn for Microsoft
PrinterOn for Microsoft
Qlik Sense Mobile
Qlik Sense Mobile
Remote Desktop
Remote Desktop
Senses
ServiceNow® Agent - Intune
ServiceNow® Agent - Intune
ServiceNow® Onboarding -Intune
ServiceNow® Onboarding -Intune
Skype for Business
Skype for Business
Smartcrypt For Intune
Speaking Email
STid Mobile ID
STid Mobile ID
Tableau Mobile for Intune
Tableau Mobile for Intune
Vera for Intune
Work Folders
Work Folders
Yammer
Yammer
ZERØ - email for attorneys
Zero for Intune
Zoom for Intune
Zoom for Intune

But when i look in the MEM portal there are no application. How is this possible?
image

Compare-IntuneBackupFile Depth Overflow

Hi jseerden,

I can see in version 2.1.1 you have addressed depth overflow issues. I get the below in version 2.0.0 and 2.1.1.

PS C:\WINDOWS\system32> Compare-IntuneBackupFile -ReferenceFilePath "C:\FilePath\FileName2.json" -DifferenceFilePath "C:\FilePath\FileName1.json"
The script failed due to call depth overflow.
+ CategoryInfo : InvalidOperation: (0:Int32) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CallDepthOverflow

Can you advise if this is a bug or user error.

[Feature request] Backup and restore client apps with the actual install package

Configuring apps in Intune GUI is pain. We're a MSP managing a lot of customers Intune tenants. Would be freaking awesome if this module could get the capability to backup and restore Win32 client apps with the install package, so that we'd only have to configure the app manually once, then add it to customer tenants in a programatically manner afterwards.

Eventually you could backup the configuration only, restore it with install package locally on the computer running the command. Something like:

"Invoke-IntuneRestoreClientApp -AppType 'Win32' -ConfigurationPath '<Path>' -PackagePath '<Path>'"

Possible in the future you think?

Issue while backing up environment

Running in some weirds issues trying to backup certain policies.

This is the error I get when backing up a Delivery Optimization policy (Profile type: Delivery Optimization)
Invoke-MSGraphRequest : 403 Forbidden
{"error":{"code":"Forbidden","message":"{\r\n "_version": 3,\r\n "Message": "Application is not authorized to perform this operation. Application must have one of the
following scopes: DeviceManagementConfiguration.ReadWrite.All - Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 - Activity ID:
2578f4a3-00e0-4699-bde7-e699fabc1086 - Url: https://fef.msub05.manage.microsoft.com/DeviceConfiguration_2203/StatelessDeviceConfigurationFEService/deviceManagement/deviceConfig
urations%28%2714381b43-02e4-474f-80e3-da9a82e80c7f%27%29/microsoft.management.services.api.getOmaSettingPlainTextValue%28secretReferenceValueId%3D%2744f6dd7c-3c9d-4cac-ab19-59d
f216dd379_14381b43-02e4-474f-80e3-da9a82e80c7f_f87fa2de-ff49-4135-b077-7c29f0a6ab70%27%29?api-version=5022-02-22",\r\n "CustomApiErrorPhrase": "",\r\n "RetryAfter":
null,\r\n "ErrorSourceService": "",\r\n "HttpHeaders":
"{}"\r\n}","innerError":{"date":"2022-03-22T12:40:15","request-id":"2578f4a3-00e0-4699-bde7-e699fabc1086","client-request-id":"2578f4a3-00e0-4699-bde7-e699fabc1086"}}}
At C:\Program Files\WindowsPowerShell\Modules\IntuneBackupAndRestore\3.2.0\Public\Invoke-IntuneBackupDeviceConfiguration.ps1:51 char:40

  • ... tingValue = Invoke-MSGraphRequest -HttpMethod GET -Url "deviceManagem ...
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : ConnectionError: (@{Request=; Response=}:PSObject) [Invoke-MSGraphRequest], HttpRequestException
    • FullyQualifiedErrorId : PowerShellGraphSDK_HttpRequestError,Microsoft.Intune.PowerShellGraphSDK.PowerShellCmdlets.InvokeRequest

Same goes for Endpoint Analytics policy and custom policy for Update Compliance.

All other policies worked fine, I connected through an application which only had read permissions

Shorten file names in Device Management Intents

Hi there!

Recently we found that we had to reduce the amount of characters in the path to Device Management Intents because the exported file names are so long (minimum of about 100 characters). From what I can tell, its _.json, add some more characters from the file path its stored at and you're looking at exceeding 256 characters.

Right now our workaround is to move it to a file path that has lesser characters. Let me know if there is any additional information needed. Thanks!

Can it be used to clone single policy?

I'm using this tool to backup and compare Intune and it is great!

Since Microsoft does not have "clone" of the policy via native GUI (in devices > Configuration Policies), can this tool be used for creating cloned copy of the policies?
Note: cloning (e.g. Duplicate) is available in new Endpoint Security folder.

400 Bad request when importing iOS Device Configuration Policies

Hi Jseerden,

After installing the newest version we are still receiving an error when importen the iOS Device Configuration file:

Invoke-IntuneRestoreDeviceCompliancePolicy : 400 Bad Request
{
"error": {
"code": "ModelValidationFailure",
"message": "An entry with type 'microsoft.management.services.api.iosGeneralDeviceConfiguration' was found, but it
is not assignable to the expected type 'microsoft.management.services.api.deviceCompliancePolicy'. The type specified
in the entry must be equal to either the expected type or a derived type.",
"innerError": {
"message": "An entry with type 'microsoft.management.services.api.iosGeneralDeviceConfiguration' was found, but
it is not assignable to the expected type 'microsoft.management.services.api.deviceCompliancePolicy'. The type
specified in the entry must be equal to either the expected type or a derived type.",
"date": "2021-02-22T13:07:28",
"request-id": "9930a335-2ec1-4e0f-af3f-d467a9eed3cb",
"client-request-id": "9930a335-2ec1-4e0f-af3f-d467a9eed3cb"
}
}
}
At C:\Program Files\WindowsPowerShell\Modules\IntuneBackupAndRestore\2.1.1\Public\Start-IntuneRestoreConfig.ps1:27
char:5

  • Invoke-IntuneRestoreDeviceCompliancePolicy -Path $Path
    
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    • FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceCompliancePo
      licy

Originally posted by @JelleMarc in #25 (comment)

Update Examples Syntax for Assignment Functions

Very minor since it is clear in the file name:

  • Examples 04 and 05 state "Invoke-IntuneRestoreDeviceCompliancePolicyAssignments" and "Invoke-IntuneRestoreDeviceConfigurationAssignments" where it should be "Invoke-IntuneRestoreDeviceCompliancePolicyAssignment" and "Invoke-IntuneRestoreDeviceConfigurationAssignment" respectively.

Thank you!

Error restoring VPN Profile: Invoke-IntuneRestoreDeviceConfiguration - [Profile Name] - Failed to restore Device Configuration (Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request)

Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request
{"error":{"code":"ModelValidationFailure","message":"An unexpected 'PrimitiveValue' node was found when reading from
the JSON reader. A 'StartArray' node was expected. Error found near:
ll,"persistent":null}],"@odata.type":"#microsoft.management.services.api.windows10VpnConfiguration"}
<---","innerError":{"message":"An unexpected 'PrimitiveValue' node was found when reading from the JSON reader. A
'StartArray' node was expected. Error found near:
ll,"persistent":null}],"@odata.type":"#microsoft.management.services.api.windows10VpnConfiguration"} <---","date"
:"2022-06-01T20:10:15","request-id":"[GUID]","client-request-id":"[GUID]"}}}
At line:1 char:1

  • Invoke-IntuneRestoreDeviceConfiguration -Path $PATH
  •   + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
      + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfiguratio
     n
    

Checking Endpoint Backup Status in Microsoft Intune Using PowerShell

I'm looking to use PowerShell to monitor the backup status of endpoints managed with Microsoft Intune and determine the percentage of endpoints with regular and successful backups. For instance, can someone guide me on how to find out what percentage of Intune-managed endpoints have completed a successful backup in the last 30 days?
I saw some commands are present to start backup, but not found anything to fetch Intune devices backup status.

Compare-IntuneBackupDirectories error if file missing in difference directory

Comparing two full backup directories while the more recent one has some items removed (for example an Intune Configuration profile has been removed since the last backup) fails ungracefully with the following error

image

Reproduction steps:

  1. Create full backup via Start-IntuneBackup (this will be the ReferenceDirectory)
  2. Make a change in Intune which would reflect as a missing JSON file in the next backup (delete a device configuration profile for example)
  3. Create another full backup via Start-IntuneBackup (this will be the DifferenceDirectory)
  4. Run a compare via Compare-IntuneBackupDirectories with the above two backup directories

Invoke-MSGraphRequest : 404 Not Found

Getting this error running the Start-IntuneBackup command.
The attached file is an image of the error message.

404 Not Found

Not sure what may be causing this error?
Could it be that the ID I am using does not have access to read the data from the console? I do have Intune Administrator role.

integer values are not being returned correctly

From Backup file

Same setting
"@odata.type": "#microsoft.graph.omaSettingInteger"
"value": "O:BAG:BAD:(A;;RC;;;BA)",

Previous Backup file back in April
"@odata.type": "#microsoft.graph.omaSettingInteger"
"value": 1,

Invoke-IntuneRestoreDeviceConfiguration > "400 Bad Request" for custom profiles that contain XML content

Restoring configuration profile fails for custom profile containing XML content (such as Windows 10 Start Layout for instance).

Example .json attached as .txt, rename to .json to use for reproduction.
Win10 StartLayout.txt

PS C:\WINDOWS\system32> $TempBackUpPath = "C:\temp\Test"
>> Invoke-IntuneRestoreDeviceConfiguration -Path $TempBackUpPath

VERBOSE: Win10 StartLayout - Failed to restore Device Configuration
Invoke-IntuneRestoreDeviceConfiguration : 400 Bad Request
{"error":{"code":"ModelValidationFailure","message":"Cannot convert the literal '<LayoutModificationTemplate [...]'
to the expected type 'Edm.Binary'.","date":"2021-09-02T08:34:13","request-id":"78a741d9-6e29-45ff-a5
37-dfeea5a79ab3","client-request-id":"78a741d9-6e29-45ff-a537-dfeea5a79ab3"}}}
At line:1 char:1
+ Invoke-IntuneRestoreDeviceConfiguration -Path $TempBackUpPath
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfiguration

Compare-IntuneBackupFile does not compare sub properties

The Compare-IntuneBackupFile function only loops through the toplevel properties.

eg:

...
"applicationGuardForceAuditing":  false,
"applicationGuardBlockClipboardSharing":  "notConfigured",
"applicationGuardAllowPrintToPDF":  false,
"bitLockerSystemDrivePolicy":  {
    "encryptionMethod":  "xtsAes128",
    "prebootRecoveryUrl":  null,
    "recoveryOptions":  {
        "blockDataRecoveryAgent":  false,
         "recoveryPasswordUsage":  "allowed",
         "recoveryKeyUsage":  "allowed",
         "hideRecoveryOptions":  true,
         "enableRecoveryInformationSaveToStore":  true,
         "recoveryInformationToStore":  "passwordAndKey",
         "enableBitLockerAfterRecoveryInformationToStore":  true
     }
},
...

in given example, everything under bitLockerSystemDrivePolicy is ignored for comparison.
would be nice if it could loop through nested properties as well

Compare-IntuneBackupDirectories - Cannot bind argument to parameter

Hi jseerden,

I'm getting the below error, can you advise if this is a bug or user error.

PS C:\WINDOWS\system32> Compare-IntuneBackupDirectories -ReferenceDirectory "C:\IntuneConfig1" -DifferenceDirectory "C:\IntuneConfig2" Compare-IntuneBackupFile : Cannot bind argument to parameter 'DifferenceFilePath' because it is an empty string.
At C:\Program Files\WindowsPowerShell\Modules\intunebackupandrestore\2.0.0\Public\Compare-IntuneBackupDirectories.ps1:62 char:94

  • ... ath $file.FileName -DifferenceFilePath $difFileFound.FullPath -ErrorA ...
  •                                        ~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidData: (:) [Compare-IntuneBackupFile], ParameterBindingValidationException
    • FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAllowed,Compare-IntuneBackupFile

Though I then also get results, so for each item it finds it seems to generate the same error above and then diff.

App Protection Policy Targeted Apps Missing

When using the latest export module (v2.1.1) I noticed that the apps I am targeting with App Protection Policy do not get exported with the rest of the JSON. If I use the PowerShell samples from Microsoft however, it exports just as it should.

Policies with brackets in their name cannot be saved

Policies with brackets in their name (eg. '[ ]') result in errors by the backup scripts

example :
Out-File : Cannot perform operation because the wildcard path C:\temp\export\Device Configurations\Assignments[Test][Windows 10] Restriction - App Store.json did not resolve to a file.

using 'Out-File -LiteralPath ...' solves the problem

400 Bad Request when importing custom settings

When trying to import Custom Settings from the Device Configuration we are receiving an error message as following:

invoke-intunerestoredeviceconfiguration : 400 Bad Request
{"error":{"code":"NotSupported","message":"{\r\n "_version": 3,\r\n "Message": "SecretReferenceValueId invalid
for create. - Operation ID (for customer support): 00000000-0000-0000-0000-000000000000 - Activity ID:
4c664761-d691-42dd-8f05-06eaa16c0f4a - Url: https://fef.amsub0502.manage.microsoft.com/DeviceConfiguration_2107/Statele
ssDeviceConfigurationFEService/deviceManagement/deviceConfigurations?api-version=5021-05-26",\r\n
"CustomApiErrorPhrase": "",\r\n "RetryAfter": null,\r\n "ErrorSourceService": "",\r\n "HttpHeaders": "{
}"\r\n}","innerError":{"date":"2021-08-06T07:38:49","request-id":"4c664761-d691-42dd-8f05-06eaa16c0f4a","client-reques
t-id":"4c664761-d691-42dd-8f05-06eaa16c0f4a"}}}
At line:1 char:1

  • invoke-intunerestoredeviceconfiguration -path C:\temp\template
  •   + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
      + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfiguration
    
    
    

It has something to do with the "secretReferenceValueId", however when trying multiple things I am unable to restore the policy. A new export/import leaves the same issue.

Write error exception restoring device config assignment

I backed up entire Intune setup from one tenant, then individually restored specific groups of settings to another tenant I manage. Although a lot of settings restored fine, I had some errors I didn't understand. This is the first:

PS C:\Users\ichil> Invoke-IntuneRestoreDeviceConfigurationAssignment -Path c:\stuff\intunebackup

VERBOSE: Intune data collection policy Intune data collection policy - Failed to restore Device Configuration Assignment(s)
Invoke-IntuneRestoreDeviceConfigurationAssignment : Cannot validate argument on parameter 'Url'. The provided URL is not valid - the URL may be a relative URL
At line:1 char:2

  • Invoke-IntuneRestoreDeviceConfigurationAssignment -Path c:\stuff\int ...
  •  + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
     + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfigurationAssignment
    
    
    

Action Type Name Path


Restore Device Configuration Assignments Offboard PC Device Configurations\Assignments\Offboard PC.json
Restore Device Configuration Assignments PBC Win 10 device restrictions Device Configurations\Assignments\PBC Win 10 device restrictions.json
Restore Device Configuration Assignments PBC Windows 10 – Chrome con... Device Configurations\Assignments\PBC Windows 10 – Chrome configur...
VERBOSE: Update policy for Windows 10 devices Update policy for Windows 10 devices - Failed to restore Device Configuration Assignment(s)
Invoke-IntuneRestoreDeviceConfigurationAssignment : Cannot validate argument on parameter 'Url'. The provided URL is not valid - the URL may be a relative URL
At line:1 char:2

  • Invoke-IntuneRestoreDeviceConfigurationAssignment -Path c:\stuff\int ...
  •  + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
     + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-IntuneRestoreDeviceConfigurationAssignment 
    
    

THE JSON FILE

{
"@odata.type": "#microsoft.graph.windowsUpdateForBusinessConfiguration",
"id": "12733dec-40fa-4bc9-8071-88ff4747d480",
"lastModifiedDateTime": "/Date(1633605466338)/",
"roleScopeTagIds": [
"0"
],
"supportsScopeTags": true,
"deviceManagementApplicabilityRuleOsEdition": null,
"deviceManagementApplicabilityRuleOsVersion": null,
"deviceManagementApplicabilityRuleDeviceMode": null,
"createdDateTime": "/Date(1633595978402)/",
"description": null,
"displayName": "Update policy for Windows 10 devices",
"version": 2,
"deliveryOptimizationMode": "httpWithPeeringNat",
"prereleaseFeatures": "userDefined",
"automaticUpdateMode": "autoInstallAtMaintenanceTime",
"microsoftUpdateServiceAllowed": true,
"driversExcluded": false,
"qualityUpdatesDeferralPeriodInDays": 0,
"featureUpdatesDeferralPeriodInDays": 0,
"qualityUpdatesPaused": false,
"featureUpdatesPaused": false,
"qualityUpdatesPauseExpiryDateTime": "/Date(-62135596800000)/",
"featureUpdatesPauseExpiryDateTime": "/Date(-62135596800000)/",
"businessReadyUpdatesOnly": "all",
"skipChecksBeforeRestart": false,
"updateWeeks": null,
"qualityUpdatesPauseStartDate": null,
"featureUpdatesPauseStartDate": null,
"featureUpdatesRollbackWindowInDays": null,
"qualityUpdatesWillBeRolledBack": null,
"featureUpdatesWillBeRolledBack": null,
"qualityUpdatesRollbackStartDateTime": "/Date(-62135596800000)/",
"featureUpdatesRollbackStartDateTime": "/Date(-62135596800000)/",
"engagedRestartDeadlineInDays": null,
"engagedRestartSnoozeScheduleInDays": null,
"engagedRestartTransitionScheduleInDays": null,
"deadlineForFeatureUpdatesInDays": null,
"deadlineForQualityUpdatesInDays": null,
"deadlineGracePeriodInDays": null,
"postponeRebootUntilAfterDeadline": null,
"autoRestartNotificationDismissal": "notConfigured",
"scheduleRestartWarningInHours": null,
"scheduleImminentRestartWarningInMinutes": null,
"userPauseAccess": "notConfigured",
"userWindowsUpdateScanAccess": "notConfigured",
"updateNotificationLevel": "notConfigured",
"allowWindows11Upgrade": false,
"installationSchedule": {
"@odata.type": "#microsoft.graph.windowsUpdateActiveHoursInstall",
"activeHoursStart": "06:00:00.0000000",
"activeHoursEnd": "22:00:00.0000000"
},
"deviceConfigurationId": "12733dec-40fa-4bc9-8071-88ff4747d480",
"deviceConfigurationODataType": "microsoft.graph.windowsUpdateForBusinessConfiguration",
"windowsUpdateForBusinessConfigurationReferenceUrl": "https://graph.microsoft.com/Beta/deviceManagement/deviceConfigurations/12733dec-40fa-4bc9-8071-88ff4747d480"
}

Unable to restore policies

_Hello,
As long as the backup is working fine, I am not able to restore a policy.
Whenever I try to restore a single policy or more than one, I get the error "Error retrieving Intune Compliance Policy for C:\Temp\Device Compliance Policies\Assignments\DemoRecovery.json. Skipping assignment restore "
Remark: I get the same behavior for all types of policies that have been backed up.
I am using the modules:
Script 2.1.1 IntuneBackupAndRestore
Binary 6.1907.1.0 Microsoft.Graph.Intune

Has anyone had this problem as well?
Thank you_

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.