├── Microsoft_Syncro_ContactCreation.ps1 ├── Microsoft_Syncro_Exchange.ps1 ├── Microsoft_Syncro_Intune.ps1 ├── Microsoft_Syncro_LicenseReport.ps1 ├── Microsoft_Syncro_MFAStatus.ps1 ├── README.md └── Syncro ├── pic1.png ├── pic10.png ├── pic11.png ├── pic12.png ├── pic13.png ├── pic14.png ├── pic15.png ├── pic2.png ├── pic3.png ├── pic4.1.png ├── pic4.png ├── pic5.png ├── pic6.png ├── pic7.png ├── pic8.png └── pic9.png /Microsoft_Syncro_ContactCreation.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | 4 | [cmdletbinding()] 5 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationId from the Secure Application Model https://github.com/KelvinTegelaar/SecureAppModel/blob/master/Create-SecureAppModel.ps1")] 6 | [string]$ApplicationId, 7 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationSecret from the Secure Application Model")] 8 | [string]$ApplicationSecret, 9 | [Parameter(Mandatory= $true, HelpMessage="Enter your Partner Tenantid")] 10 | [string]$tenantID, 11 | [Parameter(Mandatory= $true, HelpMessage="Enter your refreshToken from the Secure Application Model")] 12 | [string]$refreshToken, 13 | [Parameter(Mandatory= $true)] 14 | [string]$SyncroAPIKey, 15 | [Parameter(Mandatory= $true)] 16 | [string]$SyncroSubdomain 17 | 18 | ) 19 | 20 | # Check if the MSOnline PowerShell module has already been loaded. 21 | if ( ! ( Get-Module MSOnline) ) { 22 | # Check if the MSOnline PowerShell module is installed. 23 | if ( Get-Module -ListAvailable -Name MSOnline ) { 24 | Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..." 25 | Import-Module MsOnline 26 | } else { 27 | Install-Module MsOnline 28 | } 29 | } 30 | 31 | ###MICROSOFT SECRETS##### 32 | 33 | $ApplicationId = $ApplicationId 34 | $ApplicationSecret = $ApplicationSecret 35 | $tenantID = $tenantID 36 | $refreshToken = $refreshToken 37 | $secPas = $ApplicationSecret| ConvertTo-SecureString -AsPlainText -Force 38 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $secPas) 39 | 40 | 41 | ###Syncro Secrets#### 42 | 43 | $SyncroSubdomain = $SyncroSubdomain 44 | $SyncroAPIKey = $SyncroAPIKey 45 | 46 | 47 | ###API Permissions Needed In Syncro##### 48 | # Contacts - Import 49 | # Customers - List/Search 50 | # Customers - View Detail 51 | # Customers - Edit 52 | 53 | 54 | ###FUNCTION TO Get All Customers IN SYNCRO#### 55 | 56 | function GetAll-Customers () { 57 | 58 | <# 59 | .SYNOPSIS 60 | This function is used to get all customer records in Syncro. 61 | .DESCRIPTION 62 | The function connects to your Syncro environment and finds all customers 63 | .EXAMPLE 64 | GetAll-Customers -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 65 | Retrieves all customers 66 | .NOTES 67 | NAME: GetAll-Customers 68 | #> 69 | 70 | [cmdletbinding()] 71 | 72 | param 73 | ( 74 | [Parameter(Mandatory=$true)] 75 | [string]$SyncroSubdomain, 76 | [string]$SyncroAPIKey, 77 | [string]$page 78 | ) 79 | 80 | 81 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers?api_key=$($SyncroAPIKey)&page=$($page)" 82 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 83 | $response 84 | 85 | } 86 | 87 | 88 | 89 | ###FUNCTION TO CREATE CONTACTS IN SYNCRO#### 90 | 91 | function Create-Contact () { 92 | 93 | <# 94 | .SYNOPSIS 95 | This function is used to create a new contact in Syncro. 96 | .DESCRIPTION 97 | The function connects to your Syncro environment and adds a new contact 98 | .EXAMPLE 99 | Create-Contact -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -customerID $customerID -name $name -email $email 100 | Adds a new contact with name and email for a customer 101 | .NOTES 102 | NAME: Create-Contact 103 | #> 104 | 105 | [cmdletbinding()] 106 | 107 | param 108 | ( 109 | [Parameter(Mandatory=$true)] 110 | [string]$SyncroSubdomain, 111 | [string]$SyncroAPIKey, 112 | [string]$customerID, 113 | [string]$name, 114 | [string]$email, 115 | [string]$notes 116 | ) 117 | 118 | 119 | 120 | $NewContact =@{ 121 | customer_id = $customerID 122 | name = $name 123 | email = $email 124 | notes = $notes 125 | api_key=$SyncroAPIKey 126 | } 127 | 128 | $body = (ConvertTo-Json $NewContact) 129 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/contacts" 130 | $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType 'application/json' 131 | $response 132 | 133 | } 134 | 135 | function Update-Contact () { 136 | 137 | <# 138 | .SYNOPSIS 139 | This function is used to update a contact record in Syncro. 140 | .DESCRIPTION 141 | The function connects to your Syncro environment and updates a contact 142 | .EXAMPLE 143 | Update-Contact -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -contactId $customerID -notes $notes 144 | Adds a new contact with name and email for a customer 145 | .NOTES 146 | NAME: Update-Contact 147 | #> 148 | 149 | [cmdletbinding()] 150 | 151 | param 152 | ( 153 | [Parameter(Mandatory=$true)] 154 | [string]$SyncroSubdomain, 155 | [string]$SyncroAPIKey, 156 | [string]$contactID, 157 | [string]$notes 158 | ) 159 | 160 | 161 | 162 | $UpdateContact =@{ 163 | notes = $notes 164 | api_key=$SyncroAPIKey 165 | } 166 | 167 | $body = (ConvertTo-Json $UpdateContact) 168 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/contacts/$($contactID)" 169 | $response = Invoke-RestMethod -Uri $url -Method Put -Body $body -ContentType 'application/json' 170 | $response 171 | 172 | } 173 | 174 | ###GET All contacts in Syncro ####### 175 | 176 | function Get-Contacts () { 177 | 178 | <# 179 | .SYNOPSIS 180 | This function is used to get all contacts that exist in Syncro 181 | .DESCRIPTION 182 | The function connects to your Syncro environment and list all contacts 183 | .EXAMPLE 184 | Get-Contacts -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 185 | Gets All Available Contacts in Syncro 186 | .NOTES 187 | NAME: Get-Contacts 188 | #> 189 | 190 | [cmdletbinding()] 191 | 192 | param 193 | ( 194 | [Parameter(Mandatory=$true)] 195 | [string]$SyncroSubdomain, 196 | [string]$SyncroAPIKey, 197 | [string]$customerId, 198 | [string]$page 199 | ) 200 | 201 | 202 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers/$($customerId)?api_key=$($SyncroAPIKey)&page=$($page)" 203 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 204 | $response 205 | 206 | } 207 | 208 | 209 | ###Fnd All Syncro Customers########## 210 | Write-Host "Getting All Customers In Syncro" 211 | 212 | $page = 1 213 | $totalPageCount = (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 214 | $SyncroCustomers = Do{ 215 | (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).customers 216 | $page = $page + 1 217 | }Until ($page -gt $totalPageCount) 218 | Write-Host "Found $($SyncroCustomers.Count) Customers in Syncro" -ForegroundColor Green 219 | $CustomerObj = forEach ($customer in $SyncroCustomers) { 220 | Write-Host "Getting domain for $($customer.business_name)" 221 | $customerDomain = ($customer.email -split "@")[1] 222 | if(!$customerDomain){ 223 | Write-Host "$($customer.business_name) does not have an email on file" -ForegroundColor Red 224 | } else { 225 | Write-Host "Customer domain is $($customerDomain)" 226 | } 227 | [PSCustomObject]@{ 228 | Domain = $customerDomain 229 | customer_id = $customer.id 230 | } 231 | 232 | } 233 | 234 | 235 | 236 | ###Connect to Partner Center to get a list of customers/tenantIDs ######### 237 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 238 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 239 | 240 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 241 | 242 | $customers = Get-MsolPartnerContract -All 243 | 244 | Write-Host "Found $($customers.Count) customers in Partner Center." -ForegroundColor DarkGreen 245 | 246 | 247 | foreach ($customer in $customers) { 248 | Write-Host "Found $($customer.Name) in Partner Center" -ForegroundColor Green 249 | write-host "Collecting data for $($Customer.Name)" -ForegroundColor Green 250 | $domain = $customer.DefaultDomainName 251 | $AllDomains = Get-MsolDomain -TenantId $customer.TenantID 252 | $Users = Get-MSoluser -TenantId $customer.TenantId -All | Where {$_.UserPrincipalName -NotLike "*#EXT#*" -and $_.isLicensed -eq $true} 253 | $customer_id = ($CustomerObj | Where-Object { $_.Domain -in $AllDomains.name}).customer_id 254 | $page = 1 255 | $totalPageCount = (Get-Contacts -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 256 | $contacts = Do{ 257 | (Get-Contacts -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerId $customer_id -page $page).customer.contacts 258 | $page = $page + 1 259 | }Until ($page -gt $totalPageCount) 260 | if($customer_id){ 261 | foreach ($user in $Users) { 262 | $userExist = ($contacts| where-object {$_.email -eq $user.userprincipalName}) 263 | if(!$userExist){ 264 | Write-Host "$($user.DisplayName) doesnt exist in Syncro. Creating New Contact...." -ForegroundColor Green 265 | Create-Contact -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerID $customer_id -name $user.DisplayName -email $user.Userprincipalname 266 | } else { 267 | Write-Host "$($user.DisplayName) exist in Syncro. Updating Contact...." -ForegroundColor Yellow 268 | Update-Contact -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -contactId $userExist.id 269 | } 270 | } 271 | forEach ($contact in $contacts) { 272 | if($Users.userprincipalname -notcontains $contact.email){ 273 | Write-Host "$($contact.name) is not in Microsoft. Adding Note of Inactive in Syncro...." -ForegroundColor Red 274 | Update-Contact -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -contactId $contact.id -notes "User doesn't exist in Microsoft" 275 | } 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /Microsoft_Syncro_Exchange.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | 4 | [cmdletbinding()] 5 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationId from the Secure Application Model https://github.com/KelvinTegelaar/SecureAppModel/blob/master/Create-SecureAppModel.ps1")] 6 | [string]$ApplicationId, 7 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationSecret from the Secure Application Model")] 8 | [string]$ApplicationSecret, 9 | [Parameter(Mandatory= $true, HelpMessage="Enter your Partner Tenantid")] 10 | [string]$tenantID, 11 | [Parameter(Mandatory= $true, HelpMessage="Enter your refreshToken from the Secure Application Model")] 12 | [string]$refreshToken, 13 | [Parameter(Mandatory= $true, HelpMessage="Enter your ExchangeRefreshToken from the Secure Application Model")] 14 | [string]$ExchangeRefreshToken, 15 | [Parameter(Mandatory= $true, HelpMessage="Enter your UserPrincipalName from the Secure Application Model")] 16 | [string]$upn, 17 | [Parameter(Mandatory= $true)] 18 | [string]$SyncroAPIKey, 19 | [Parameter(Mandatory= $true)] 20 | [string]$SyncroSubdomain 21 | 22 | ) 23 | 24 | # Check if the MSOnline PowerShell module has already been loaded. 25 | if ( ! ( Get-Module MSOnline) ) { 26 | # Check if the MSOnline PowerShell module is installed. 27 | if ( Get-Module -ListAvailable -Name MSOnline ) { 28 | Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..." 29 | Import-Module MsOnline 30 | } else { 31 | Install-Module MsOnline 32 | } 33 | } 34 | 35 | ###MICROSOFT SECRETS##### 36 | 37 | $ApplicationId = $ApplicationId 38 | $ApplicationSecret = $ApplicationSecret 39 | $tenantID = $tenantID 40 | $refreshToken = $refreshToken 41 | $ExchangeRefreshToken = $ExchangeRefreshToken 42 | $upn = $upn 43 | $secPas = $ApplicationSecret| ConvertTo-SecureString -AsPlainText -Force 44 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $secPas) 45 | 46 | 47 | ###Syncro Secrets#### 48 | 49 | $SyncroSubdomain = $SyncroSubdomain 50 | $SyncroAPIKey = $SyncroAPIKey 51 | 52 | 53 | ###API Permissions Needed In Syncro##### 54 | # Customers - List/Search 55 | # Customers - View Detail 56 | # Customers - Edit 57 | # Documentation - Allow Usage 58 | # Documentation - Create 59 | # Documentation - Edit 60 | 61 | ###FUNCTION TO Get All Customers IN SYNCRO#### 62 | 63 | function GetAll-Customers () { 64 | 65 | <# 66 | .SYNOPSIS 67 | This function is used to get all customer records in Syncro. 68 | .DESCRIPTION 69 | The function connects to your Syncro environment and finds all customers 70 | .EXAMPLE 71 | GetAll-Customers -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 72 | Retrieves all customers 73 | .NOTES 74 | NAME: GetAll-Customers 75 | #> 76 | 77 | [cmdletbinding()] 78 | 79 | param 80 | ( 81 | [Parameter(Mandatory=$true)] 82 | [string]$SyncroSubdomain, 83 | [string]$SyncroAPIKey, 84 | [string]$page 85 | ) 86 | 87 | 88 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers?api_key=$($SyncroAPIKey)&page=$($page)" 89 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 90 | $response 91 | 92 | } 93 | 94 | 95 | 96 | ###Update Documents in Syncro ####### 97 | 98 | function Update-WikiPage () { 99 | 100 | <# 101 | .SYNOPSIS 102 | This function is used to update a document in Syncro. 103 | .DESCRIPTION 104 | The function connects to your Syncro environment and updates a document 105 | .EXAMPLE 106 | Update-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -WikiID $WikiID -body $body 107 | Updates document with new body. 108 | .NOTES 109 | NAME: Update-WikiPage 110 | #> 111 | 112 | [cmdletbinding()] 113 | 114 | param 115 | ( 116 | [Parameter(Mandatory=$true)] 117 | [string]$SyncroSubdomain, 118 | [string]$SyncroAPIKey, 119 | [string]$WikiID, 120 | [string]$body 121 | ) 122 | 123 | 124 | 125 | $UpdateWikiPage =@{ 126 | body = $body 127 | api_key=$SyncroAPIKey 128 | } 129 | 130 | $body = (ConvertTo-Json $UpdateWikiPage) 131 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages/$($WikiID)" 132 | $response = Invoke-RestMethod -Uri $url -Method Put -Body $body -ContentType 'application/json' 133 | $response 134 | 135 | } 136 | 137 | ###Create new document in Syncro ####### 138 | 139 | function Create-WikiPage () { 140 | 141 | <# 142 | .SYNOPSIS 143 | This function is used to create a document for a customer in Syncro. 144 | .DESCRIPTION 145 | The function connects to your Syncro environment and creates a document for a customer 146 | .EXAMPLE 147 | Create-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -customerID $customerID -name $name -body $body 148 | Creates a new document for a customer in Syncro 149 | .NOTES 150 | NAME: Create-WikiPage 151 | #> 152 | 153 | [cmdletbinding()] 154 | 155 | param 156 | ( 157 | [Parameter(Mandatory=$true)] 158 | [string]$SyncroSubdomain, 159 | [string]$SyncroAPIKey, 160 | [string]$customerID, 161 | [string]$name, 162 | $body 163 | ) 164 | 165 | 166 | 167 | $CreatePage =@{ 168 | customer_id = $customerID 169 | name = $name 170 | body= $body 171 | api_key=$SyncroAPIKey 172 | } 173 | 174 | $body = (ConvertTo-Json $CreatePage) 175 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages" 176 | $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType 'application/json' 177 | $response 178 | 179 | } 180 | 181 | ###GET All Documentsin Syncro ####### 182 | 183 | function Get-WikiPage () { 184 | 185 | <# 186 | .SYNOPSIS 187 | This function is used to get all documents within Syncro 188 | .DESCRIPTION 189 | The function connects to your Syncro environment and gets all documents 190 | .EXAMPLE 191 | Get-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 192 | Gets all documents that exist 193 | .NOTES 194 | NAME: Get-WikiPage 195 | #> 196 | 197 | [cmdletbinding()] 198 | 199 | param 200 | ( 201 | [Parameter(Mandatory=$true)] 202 | [string]$SyncroSubdomain, 203 | [string]$SyncroAPIKey, 204 | [string]$page 205 | ) 206 | 207 | 208 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages?api_key=$($SyncroAPIKey)&page=$($page)" 209 | $response = Invoke-RestMethod -Uri $url -Method GET -ContentType 'application/json' 210 | $response 211 | 212 | } 213 | 214 | 215 | 216 | ###Fnd All Syncro Customers########## 217 | Write-Host "Getting All Customers In Syncro" 218 | 219 | $page = 1 220 | $totalPageCount = (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 221 | $SyncroCustomers = Do{ 222 | (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).customers 223 | $page = $page + 1 224 | }Until ($page -gt $totalPageCount) 225 | Write-Host "Found $($SyncroCustomers.Count) Customers in Syncro" -ForegroundColor Green 226 | $CustomerObj = forEach ($customer in $SyncroCustomers) { 227 | Write-Host "Getting domain for $($customer.business_name)" 228 | $customerDomain = ($customer.email -split "@")[1] 229 | if(!$customerDomain){ 230 | Write-Host "$($customer.business_name) does not have an email on file" -ForegroundColor Red 231 | } else { 232 | Write-Host "Customer domain is $($customerDomain)" 233 | } 234 | [PSCustomObject]@{ 235 | Domain = $customerDomain 236 | customer_id = $customer.id 237 | } 238 | 239 | } 240 | 241 | ###Connect to your Own Partner Center to get a list of customers/tenantIDs ######### 242 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 243 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 244 | 245 | 246 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 247 | 248 | $customers = Get-MsolPartnerContract -All 249 | 250 | Write-Host "Found $($customers.Count) customers in Partner Center." -ForegroundColor DarkGreen 251 | 252 | 253 | foreach ($customer in $customers) { 254 | 255 | #Get ALL Licensed Users and Find Shared Mailboxes# 256 | Write-Host "Checking Mailboxes for $($Customer.Name)" -ForegroundColor Green 257 | try{ 258 | $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes 'https://outlook.office365.com/.default' -Tenant $customer.TenantId -ErrorAction SilentlyContinue 259 | $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force 260 | $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue) 261 | $domain = $customer.DefaultDomainName 262 | $AllDomains = Get-MsolDomain -TenantId $customer.TenantID 263 | $InitialDomain = Get-MsolDomain -TenantId $customer.TenantId | Where-Object {$_.IsInitial -eq $true} 264 | $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.outlook.com/powershell-liveid?DelegatedOrg=$($InitialDomain)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection -ErrorAction SilentlyContinue 265 | Import-PSSession $session 266 | }catch{("This tenant does not have exchange")} 267 | try{ 268 | Write-Host "Geting ALl Mailbox information, this will take a few minutes" 269 | $mailboxes = Get-Mailbox | Get-MailboxStatistics | Select-Object DisplayName, @{name=”TotalItemSize (GB)”;expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split(“(“)[1].Split(” “)[0].Replace(“,”,””)/1GB),2)}},ItemCount,LastLogonTime | Sort “TotalItemSize (GB)” -Descending 270 | $mailflowRules = Get-TransportRule | Select-Object Name 271 | $DkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled 272 | $ATPSettings = Get-AtpPolicyForO365 | Select-Object Name, EnableSafeLinksForO365Clients, EnableATPForSPOTeamsODB 273 | $SafeLinksPolicy = Get-SafeLinksPolicy | Select-Object Name, isEnabled, isDefault 274 | $SafeAttachmentPolicy = Get-SafeAttachmentPolicy | Select-Object Name, Action, Enable 275 | }catch{("This tenant does not have ATP licensing")} 276 | try{ 277 | Remove-PSSession $session}catch{("There is no session to Remove")} 278 | $customer_id = ($CustomerObj | Where-Object { $_.Domain -in $AllDomains.name}).customer_id 279 | $page = 1 280 | $totaldocCount = (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 281 | $CurrentDocuments = Do{ 282 | (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).wiki_pages 283 | $page = $page + 1 284 | }Until ($page -gt $totaldocCount) 285 | $name = "Microsoft Exchange Report: $($customer.Name)" 286 | if($customer_id){ 287 | $bodyVariables = @{ 288 | "customer_Name" = $customer.Name 289 | "Tenant_ID" = $customer.TenantID 290 | "Mailboxes" = ($mailboxes| convertto-html -Fragment | out-string) 291 | "mailflow_rules" = ($mailflowRules | convertto-html -Fragment | out-string) 292 | "DkimConfig" = ($DkimConfig | convertto-html -Fragment | out-string) 293 | "ATPSettings" = ($ATPSettings | convertto-html -Fragment | out-string) 294 | "SafeLinksPolicy" = ($SafeLinksPolicy | convertto-html -Fragment | out-string) 295 | "SafeAttachmentPolicy" = ($SafeAttachmentPolicy | convertto-html -Fragment | out-string) 296 | 297 | } 298 | $body = "

Customer Name: $($bodyVariables.customer_Name)

299 | 300 |

TenantID: $($bodyVariables.Tenant_ID)

301 | 302 |

Mailboxes: $($bodyVariables.Mailboxes)

303 | 304 |

Mailflow Rules: $($bodyVariables.mailflow_rules)

305 | 306 |

DKIM Configuration: $($bodyVariables.DkimConfig)

307 | 308 |

ATP Settings: $($bodyVariables.ATPSettings)

309 | 310 |

Safe Links Policies: $($bodyVariables.SafeLinksPolicy)

311 | 312 |

Safe Attachment Policies: $($bodyVariables.SafeAttachmentPolicy)

" 313 | 314 | $docExist = ($CurrentDocuments | where-object {$_.name -eq $name}) 315 | 316 | if(!$docExist){ 317 | Write-Host "Creating a new document" -ForegroundColor Green 318 | Create-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerID $customer_id -name $name -body $body 319 | } else { 320 | Write-Host "Document already exist. Updating Existing Document" -ForegroundColor Yellow 321 | Update-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -WikiID $docExist.id -body $body 322 | } 323 | } 324 | } 325 | -------------------------------------------------------------------------------- /Microsoft_Syncro_Intune.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | 4 | [cmdletbinding()] 5 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationId from the Secure Application Model https://github.com/KelvinTegelaar/SecureAppModel/blob/master/Create-SecureAppModel.ps1")] 6 | [string]$ApplicationId, 7 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationSecret from the Secure Application Model")] 8 | [string]$ApplicationSecret, 9 | [Parameter(Mandatory= $true, HelpMessage="Enter your Partner Tenantid")] 10 | [string]$tenantID, 11 | [Parameter(Mandatory= $true, HelpMessage="Enter your refreshToken from the Secure Application Model")] 12 | [string]$refreshToken, 13 | [Parameter(Mandatory= $true, HelpMessage="Enter your ExchangeRefreshToken from the Secure Application Model")] 14 | [string]$ExchangeRefreshToken, 15 | [Parameter(Mandatory= $true, HelpMessage="Enter your UserPrincipalName from the Secure Application Model")] 16 | [string]$upn, 17 | [Parameter(Mandatory= $true)] 18 | [string]$SyncroAPIKey, 19 | [Parameter(Mandatory= $true)] 20 | [string]$SyncroSubdomain 21 | 22 | ) 23 | 24 | # Check if the MSOnline PowerShell module has already been loaded. 25 | if ( ! ( Get-Module MSOnline) ) { 26 | # Check if the MSOnline PowerShell module is installed. 27 | if ( Get-Module -ListAvailable -Name MSOnline ) { 28 | Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..." 29 | Import-Module MsOnline 30 | } else { 31 | Install-Module MsOnline 32 | } 33 | } 34 | 35 | ###MICROSOFT SECRETS##### 36 | 37 | $ApplicationId = $ApplicationId 38 | $ApplicationSecret = $ApplicationSecret 39 | $tenantID = $tenantID 40 | $refreshToken = $refreshToken 41 | $ExchangeRefreshToken = $ExchangeRefreshToken 42 | $upn = $upn 43 | $secPas = $ApplicationSecret| ConvertTo-SecureString -AsPlainText -Force 44 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $secPas) 45 | 46 | 47 | ###Additional API Permissions Need for App in Azure AD#### 48 | #DeviceManagementApps.Read.All 49 | #Device.Read.All 50 | #DeviceManagementConfiguration.Read.All 51 | #DeviceManagementManagedDevices.Read.All 52 | 53 | 54 | 55 | ###Syncro Secrets#### 56 | 57 | $SyncroSubdomain = $SyncroSubdomain 58 | $SyncroAPIKey = $SyncroAPIKey 59 | 60 | 61 | ###API Permissions Needed In Syncro##### 62 | # Customers - List/Search 63 | # Customers - View Detail 64 | # Customers - Edit 65 | # Documentation - Allow Usage 66 | # Documentation - Create 67 | # Documentation - Edit 68 | 69 | 70 | ###FUNCTION TO Get All Customers IN SYNCRO#### 71 | 72 | function GetAll-Customers () { 73 | 74 | <# 75 | .SYNOPSIS 76 | This function is used to get all customer records in Syncro. 77 | .DESCRIPTION 78 | The function connects to your Syncro environment and finds all customers 79 | .EXAMPLE 80 | GetAll-Customers -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 81 | Retrieves all customers 82 | .NOTES 83 | NAME: GetAll-Customers 84 | #> 85 | 86 | [cmdletbinding()] 87 | 88 | param 89 | ( 90 | [Parameter(Mandatory=$true)] 91 | [string]$SyncroSubdomain, 92 | [string]$SyncroAPIKey, 93 | [string]$page 94 | ) 95 | 96 | 97 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers?api_key=$($SyncroAPIKey)&page=$($page)" 98 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 99 | $response 100 | 101 | } 102 | 103 | ###Update Documents in Syncro ####### 104 | 105 | function Update-WikiPage () { 106 | 107 | <# 108 | .SYNOPSIS 109 | This function is used to update a document in Syncro. 110 | .DESCRIPTION 111 | The function connects to your Syncro environment and updates a document 112 | .EXAMPLE 113 | Update-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -WikiID $WikiID -body $body 114 | Updates document with new body. 115 | .NOTES 116 | NAME: Update-WikiPage 117 | #> 118 | 119 | [cmdletbinding()] 120 | 121 | param 122 | ( 123 | [Parameter(Mandatory=$true)] 124 | [string]$SyncroSubdomain, 125 | [string]$SyncroAPIKey, 126 | [string]$WikiID, 127 | [string]$body 128 | ) 129 | 130 | 131 | 132 | $UpdateWikiPage =@{ 133 | body = $body 134 | api_key=$SyncroAPIKey 135 | } 136 | 137 | $body = (ConvertTo-Json $UpdateWikiPage) 138 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages/$($WikiID)" 139 | $response = Invoke-RestMethod -Uri $url -Method Put -Body $body -ContentType 'application/json' 140 | $response 141 | 142 | } 143 | 144 | ###Create new document in Syncro ####### 145 | 146 | function Create-WikiPage () { 147 | 148 | <# 149 | .SYNOPSIS 150 | This function is used to create a document for a customer in Syncro. 151 | .DESCRIPTION 152 | The function connects to your Syncro environment and creates a document for a customer 153 | .EXAMPLE 154 | Create-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -customerID $customerID -name $name -body $body 155 | Creates a new document for a customer in Syncro 156 | .NOTES 157 | NAME: Create-WikiPage 158 | #> 159 | 160 | [cmdletbinding()] 161 | 162 | param 163 | ( 164 | [Parameter(Mandatory=$true)] 165 | [string]$SyncroSubdomain, 166 | [string]$SyncroAPIKey, 167 | [string]$customerID, 168 | [string]$name, 169 | $body 170 | ) 171 | 172 | 173 | 174 | $CreatePage =@{ 175 | customer_id = $customerID 176 | name = $name 177 | body= $body 178 | api_key=$SyncroAPIKey 179 | } 180 | 181 | $body = (ConvertTo-Json $CreatePage) 182 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages" 183 | $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType 'application/json' 184 | $response 185 | 186 | } 187 | 188 | ###GET All Documentsin Syncro ####### 189 | 190 | function Get-WikiPage () { 191 | 192 | <# 193 | .SYNOPSIS 194 | This function is used to get all documents within Syncro 195 | .DESCRIPTION 196 | The function connects to your Syncro environment and gets all documents 197 | .EXAMPLE 198 | Get-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 199 | Gets all documents that exist 200 | .NOTES 201 | NAME: Get-WikiPage 202 | #> 203 | 204 | [cmdletbinding()] 205 | 206 | param 207 | ( 208 | [Parameter(Mandatory=$true)] 209 | [string]$SyncroSubdomain, 210 | [string]$SyncroAPIKey, 211 | [string]$page 212 | ) 213 | 214 | 215 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages?api_key=$($SyncroAPIKey)&page=$($page)" 216 | $response = Invoke-RestMethod -Uri $url -Method GET -ContentType 'application/json' 217 | $response 218 | 219 | } 220 | 221 | 222 | 223 | ###Fnd All Syncro Customers########## 224 | Write-Host "Getting All Customers In Syncro" 225 | 226 | $page = 1 227 | $totalPageCount = (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 228 | $SyncroCustomers = Do{ 229 | (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).customers 230 | $page = $page + 1 231 | }Until ($page -gt $totalPageCount) 232 | Write-Host "Found $($SyncroCustomers.Count) Customers in Syncro" -ForegroundColor Green 233 | $CustomerObj = forEach ($customer in $SyncroCustomers) { 234 | Write-Host "Getting domain for $($customer.business_name)" 235 | $customerDomain = ($customer.email -split "@")[1] 236 | if(!$customerDomain){ 237 | Write-Host "$($customer.business_name) does not have an email on file" -ForegroundColor Red 238 | } else { 239 | Write-Host "Customer domain is $($customerDomain)" 240 | } 241 | [PSCustomObject]@{ 242 | Domain = $customerDomain 243 | customer_id = $customer.id 244 | } 245 | 246 | } 247 | 248 | ###Connect to your Own Partner Center to get a list of customers/tenantIDs ######### 249 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 250 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 251 | 252 | 253 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 254 | 255 | $customers = Get-MsolPartnerContract -All 256 | 257 | Write-Host "Found $($customers.Count) customers in Partner Center." -ForegroundColor DarkGreen 258 | foreach ($customer in $customers) { 259 | Write-Host "Found $($customer.Name) in Partner Center" -ForegroundColor Green 260 | 261 | ###Get Access Token######## 262 | $CustomerToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -Tenant $customer.TenantID 263 | $headers = @{ "Authorization" = "Bearer $($CustomerToken.AccessToken)" } 264 | $domain = $customer.DefaultDomainName 265 | $AllDomains = Get-MsolDomain -TenantId $customer.TenantID 266 | 267 | #####Get Intune information if it is available#### 268 | try{ 269 | $Devices = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/beta/deviceManagement/managedDevices' -Headers $headers -Method Get -ContentType "application/json").value | Select-Object deviceName, ownerType, operatingSystem, osVersion, complianceState,userPrincipalName, autopilotEnrolled,isEncrypted 270 | $complianceSummary = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/beta/deviceManagement/deviceCompliancePolicyDeviceStateSummary' -Headers $headers -Method Get -ContentType "application/json") | Select-Object inGracePeriodCount, compliantDeviceCount, nonCompliantDeviceCount 271 | $apps = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/beta/deviceAppManagement/mobileApps?$filter=(microsoft.graph.managedApp/appAvailability eq null or isAssigned eq true)&$orderby=displayName' -Headers $headers -Method Get -ContentType "application/json").value | Select-Object displayName 272 | }catch{("Either this tenant does not have Intune licensing or you have not given proper permissions to the app listed at the begining of this script") 273 | continue} 274 | 275 | #####Create or Update Documentation in Snycro#### 276 | if($Devices){ 277 | $customer_id = ($CustomerObj | Where-Object { $_.Domain -in $AllDomains.name}).customer_id 278 | $page = 1 279 | $totaldocCount = (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 280 | $CurrentDocuments = Do{ 281 | (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).wiki_pages 282 | $page = $page + 1 283 | }Until ($page -gt $totaldocCount) 284 | $name = "Microsoft Intune Report: $($customer.Name)" 285 | if($customer_id){ 286 | $bodyVariables = @{ 287 | "customer_Name" = $customer.Name 288 | "Tenant_ID" = $customer.TenantID 289 | "Devices" = ($Devices | convertto-html -Fragment | out-string) 290 | "complianceSummary" = ($complianceSummary | convertto-html -Fragment | out-string) 291 | "apps" = ($apps | convertto-html -Fragment | out-string) 292 | 293 | } 294 | $body = "

Customer Name: $($bodyVariables.customer_Name)

295 | 296 |

TenantID: $($bodyVariables.Tenant_ID)

297 | 298 |

Compliance Summary: $($bodyVariables.complianceSummary)

299 | 300 |

Devices: $($bodyVariables.Devices)

301 | 302 |

Apps: $($bodyVariables.apps)

" 303 | 304 | $docExist = ($CurrentDocuments | where-object {$_.name -eq $name}) 305 | 306 | if(!$docExist){ 307 | Write-Host "Creating a new document" -ForegroundColor Green 308 | Create-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerID $customer_id -name $name -body $body 309 | } else { 310 | Write-Host "Document already exist. Updating Existing Document" -ForegroundColor Yellow 311 | Update-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -WikiID $docExist.id -body $body 312 | } 313 | 314 | } 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /Microsoft_Syncro_LicenseReport.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | 4 | [cmdletbinding()] 5 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationId from the Secure Application Model https://github.com/KelvinTegelaar/SecureAppModel/blob/master/Create-SecureAppModel.ps1")] 6 | [string]$ApplicationId, 7 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationSecret from the Secure Application Model")] 8 | [string]$ApplicationSecret, 9 | [Parameter(Mandatory= $true, HelpMessage="Enter your Partner Tenantid")] 10 | [string]$tenantID, 11 | [Parameter(Mandatory= $true, HelpMessage="Enter your refreshToken from the Secure Application Model")] 12 | [string]$refreshToken, 13 | [Parameter(Mandatory= $true)] 14 | [string]$SyncroAPIKey, 15 | [Parameter(Mandatory= $true)] 16 | [string]$SyncroSubdomain 17 | 18 | ) 19 | 20 | # Check if the MSOnline PowerShell module has already been loaded. 21 | if ( ! ( Get-Module MSOnline) ) { 22 | # Check if the MSOnline PowerShell module is installed. 23 | if ( Get-Module -ListAvailable -Name MSOnline ) { 24 | Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..." 25 | Import-Module MsOnline 26 | } else { 27 | Install-Module MsOnline 28 | } 29 | } 30 | 31 | ###MICROSOFT SECRETS##### 32 | 33 | $ApplicationId = $ApplicationId 34 | $ApplicationSecret = $ApplicationSecret 35 | $tenantID = $tenantID 36 | $refreshToken = $refreshToken 37 | $secPas = $ApplicationSecret| ConvertTo-SecureString -AsPlainText -Force 38 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $secPas) 39 | 40 | 41 | ###Syncro Secrets#### 42 | 43 | $SyncroSubdomain = $SyncroSubdomain 44 | $SyncroAPIKey = $SyncroAPIKey 45 | 46 | 47 | ###API Permissions Needed In Syncro##### 48 | # Customers - List/Search 49 | # Customers - View Detail 50 | # Customers - Edit 51 | # Documentation - Allow Usage 52 | # Documentation - Create 53 | # Documentation - Edit 54 | 55 | 56 | 57 | ###FUNCTION TO Get All Customers IN SYNCRO#### 58 | function GetAll-Customers () { 59 | 60 | <# 61 | .SYNOPSIS 62 | This function is used to get all customer records in Syncro. 63 | .DESCRIPTION 64 | The function connects to your Syncro environment and finds all customers 65 | .EXAMPLE 66 | GetAll-Customers -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 67 | Retrieves all customers 68 | .NOTES 69 | NAME: GetAll-Customers 70 | #> 71 | 72 | [cmdletbinding()] 73 | 74 | param 75 | ( 76 | [Parameter(Mandatory=$true)] 77 | [string]$SyncroSubdomain, 78 | [string]$SyncroAPIKey, 79 | [string]$page 80 | ) 81 | 82 | 83 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers?api_key=$($SyncroAPIKey)&page=$($page)" 84 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 85 | $response 86 | 87 | } 88 | 89 | 90 | 91 | ###Update Documents in Syncro ####### 92 | 93 | function Update-WikiPage () { 94 | 95 | <# 96 | .SYNOPSIS 97 | This function is used to update a document in Syncro. 98 | .DESCRIPTION 99 | The function connects to your Syncro environment and updates a document 100 | .EXAMPLE 101 | Update-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -WikiID $WikiID -body $body 102 | Updates document with new body. 103 | .NOTES 104 | NAME: Update-WikiPage 105 | #> 106 | 107 | [cmdletbinding()] 108 | 109 | param 110 | ( 111 | [Parameter(Mandatory=$true)] 112 | [string]$SyncroSubdomain, 113 | [string]$SyncroAPIKey, 114 | [string]$WikiID, 115 | [string]$body 116 | ) 117 | 118 | 119 | 120 | $UpdateWikiPage =@{ 121 | body = $body 122 | api_key=$SyncroAPIKey 123 | } 124 | 125 | $body = (ConvertTo-Json $UpdateWikiPage) 126 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages/$($WikiID)" 127 | $response = Invoke-RestMethod -Uri $url -Method Put -Body $body -ContentType 'application/json' 128 | $response 129 | 130 | } 131 | 132 | ###Create new document in Syncro ####### 133 | 134 | function Create-WikiPage () { 135 | 136 | <# 137 | .SYNOPSIS 138 | This function is used to create a document for a customer in Syncro. 139 | .DESCRIPTION 140 | The function connects to your Syncro environment and creates a document for a customer 141 | .EXAMPLE 142 | Create-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -customerID $customerID -name $name -body $body 143 | Creates a new document for a customer in Syncro 144 | .NOTES 145 | NAME: Create-WikiPage 146 | #> 147 | 148 | [cmdletbinding()] 149 | 150 | param 151 | ( 152 | [Parameter(Mandatory=$true)] 153 | [string]$SyncroSubdomain, 154 | [string]$SyncroAPIKey, 155 | [string]$customerID, 156 | [string]$name, 157 | $body 158 | ) 159 | 160 | 161 | 162 | $CreatePage =@{ 163 | customer_id = $customerID 164 | name = $name 165 | body= $body 166 | api_key=$SyncroAPIKey 167 | } 168 | 169 | $body = (ConvertTo-Json $CreatePage) 170 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages" 171 | $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType 'application/json' 172 | $response 173 | 174 | } 175 | 176 | ###GET All Documentsin Syncro ####### 177 | 178 | function Get-WikiPage () { 179 | 180 | <# 181 | .SYNOPSIS 182 | This function is used to get all documents within Syncro 183 | .DESCRIPTION 184 | The function connects to your Syncro environment and gets all documents 185 | .EXAMPLE 186 | Get-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 187 | Gets all documents that exist 188 | .NOTES 189 | NAME: Get-WikiPage 190 | #> 191 | 192 | [cmdletbinding()] 193 | 194 | param 195 | ( 196 | [Parameter(Mandatory=$true)] 197 | [string]$SyncroSubdomain, 198 | [string]$SyncroAPIKey, 199 | [string]$page 200 | ) 201 | 202 | 203 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages?api_key=$($SyncroAPIKey)&page=$($page)" 204 | $response = Invoke-RestMethod -Uri $url -Method GET -ContentType 'application/json' 205 | $response 206 | 207 | } 208 | 209 | 210 | 211 | ###Fnd All Syncro Customers########## 212 | Write-Host "Getting All Customers In Syncro" 213 | 214 | $page = 1 215 | $totalPageCount = (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 216 | $SyncroCustomers = Do{ 217 | (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).customers 218 | $page = $page + 1 219 | }Until ($page -gt $totalPageCount) 220 | Write-Host "Found $($SyncroCustomers.Count) Customers in Syncro" -ForegroundColor Green 221 | $CustomerObj = forEach ($customer in $SyncroCustomers) { 222 | Write-Host "Getting domain for $($customer.business_name)" 223 | $customerDomain = ($customer.email -split "@")[1] 224 | if(!$customerDomain){ 225 | Write-Host "$($customer.business_name) does not have an email on file" -ForegroundColor Red 226 | } else { 227 | Write-Host "Customer domain is $($customerDomain)" 228 | } 229 | [PSCustomObject]@{ 230 | Domain = $customerDomain 231 | customer_id = $customer.id 232 | } 233 | 234 | } 235 | 236 | 237 | 238 | #Account SKUs to transform to normal name. 239 | $AccountSkuIdDecodeData = @{ 240 | "SPB" = "Micorsoft 365 Business Premium" 241 | "SMB_BUSINESS" = "MICROSOFT 365 APPS FOR BUSINESS" 242 | "SMB_BUSINESS_ESSENTIALS" = "MICROSOFT 365 BUSINESS BASIC" 243 | "M365_F1" = "Microsoft 365 F1" 244 | "O365_BUSINESS_ESSENTIALS" = "MICROSOFT 365 BUSINESS BASIC" 245 | "O365_BUSINESS_PREMIUM" = "MICROSOFT 365 BUSINESS STANDARD" 246 | "DESKLESSPACK" = "OFFICE 365 F3" 247 | "TEAMS_FREE" = "MICROSOFT TEAM (FREE)" 248 | "TEAMS_EXPLORATORY" = "MICROSOFT TEAMS EXPLORATORY" 249 | "M365EDU_A3_STUDENT" = "MICROSOFT 365 A3 FOR STUDENTS" 250 | "M365EDU_A5_STUDENT" = "MICROSOFT 365 A5 FOR STUDENTS" 251 | "M365EDU_A3_FACULTY" = "MICROSOFT 365 A3 FOR FACULTY" 252 | "M365EDU_A5_FACULTY" = "MICROSOFT 365 A5 FOR FACULTY" 253 | "MCOEV_FACULTY" = "MICROSOFT 365 PHONE SYSTEM FOR FACULTY" 254 | "MCOEV_STUDENT" = "MICROSOFT 365 PHONE SYSTEM FOR STUDENTS" 255 | "ENTERPRISEPREMIUM_STUDENT" = "Office 365 A5 for students" 256 | "ENTERPRISEPREMIUM_FACULTY" = "Office 365 A5 for faculty" 257 | "M365EDU_A1" = "Microsoft 365 A1" 258 | "SHAREPOINTSTANDARD" = "SHAREPOINT ONLINE (PLAN 1)" 259 | "SHAREPOINTENTERPRISE" = "SHAREPOINT ONLINE (PLAN 2)" 260 | "EXCHANGEDESKLESS" = "EXCHANGE ONLINE KIOSK" 261 | "LITEPACK" = "OFFICE 365 SMALL BUSINESS" 262 | "EXCHANGESTANDARD" = "EXCHANGE ONLINE (PLAN 1)" 263 | "STANDARDPACK" = "OFFICE 365 E1" 264 | "STANDARDWOFFPACK" = "Office 365 (Plan E2)" 265 | "ENTERPRISEPACK" = "OFFICE 365 E3" 266 | "VISIOCLIENT" = "Visio Pro Online" 267 | "POWER_BI_ADDON" = "Office 365 Power BI Addon" 268 | "POWER_BI_INDIVIDUAL_USE" = "Power BI Individual User" 269 | "POWER_BI_STANDALONE" = "Power BI Stand Alone" 270 | "POWER_BI_STANDARD" = "Power-BI Standard" 271 | "PROJECTESSENTIALS" = "Project Lite" 272 | "PROJECTCLIENT" = "Project Professional" 273 | "PROJECTONLINE_PLAN_1" = "Project Online" 274 | "PROJECTONLINE_PLAN_2" = "Project Online and PRO" 275 | "ProjectPremium" = "Project Online Premium" 276 | "EMS" = "ENTERPRISE MOBILITY + SECURITY E3" 277 | "EMSPREMIUM" = "ENTERPRISE MOBILITY + SECURITY E5" 278 | "RIGHTSMANAGEMENT" = "AZURE INFORMATION PROTECTION PLAN 1" 279 | "MCOMEETADV" = "Microsoft 365 Audio Conferencing" 280 | "BI_AZURE_P1" = "POWER BI FOR OFFICE 365 ADD-ON" 281 | "INTUNE_A" = "INTUNE" 282 | "WIN_DEF_ATP" = "Microsoft Defender Advanced Threat Protection" 283 | "IDENTITY_THREAT_PROTECTION" = "Microsoft 365 E5 Security" 284 | "IDENTITY_THREAT_PROTECTION_FOR_EMS_E5" = "Microsoft 365 E5 Security for EMS E5" 285 | "ATP_ENTERPRISE" = "Office 365 Advanced Threat Protection (Plan 1)" 286 | "EQUIVIO_ANALYTICS" = "Office 365 Advanced eDiscovery" 287 | "AAD_BASIC" = "Azure Active Directory Basic" 288 | "RMS_S_ENTERPRISE" = "Azure Active Directory Rights Management" 289 | "AAD_PREMIUM" = "Azure Active Directory Premium" 290 | "STANDARDPACK_GOV" = "Microsoft Office 365 (Plan G1) for Government" 291 | "M365_G3_GOV" = "MICROSOFT 365 GCC G3" 292 | "ENTERPRISEPACK_USGOV_DOD" = "Office 365 E3_USGOV_DOD" 293 | "ENTERPRISEPACK_USGOV_GCCHIGH" = "Office 365 E3_USGOV_GCCHIGH" 294 | "ENTERPRISEPACK_GOV" = "OFFICE 365 GCC G3" 295 | "SHAREPOINTLITE" = "SharePoint Online (Plan 1)" 296 | "MCOIMP" = "SKYPE FOR BUSINESS ONLINE (PLAN 1)" 297 | "OFFICESUBSCRIPTION" = "MICROSOFT 365 APPS FOR ENTERPRISE" 298 | "YAMMER_MIDSIZE" = "Yammer" 299 | "DYN365_ENTERPRISE_PLAN1" = "Dynamics 365 Customer Engagement Plan Enterprise Edition" 300 | "ENTERPRISEPREMIUM_NOPSTNCONF" = "Enterprise E5 (without Audio Conferencing)" 301 | "ENTERPRISEPREMIUM" = "Enterprise E5 (with Audio Conferencing)" 302 | "MCOSTANDARD" = "Skype for Business Online Standalone Plan 2" 303 | "PROJECT_MADEIRA_PREVIEW_IW_SKU" = "Dynamics 365 for Financials for IWs" 304 | "EOP_ENTERPRISE_FACULTY" = "Exchange Online Protection for Faculty" 305 | "DYN365_FINANCIALS_BUSINESS_SKU" = "Dynamics 365 for Financials Business Edition" 306 | "DYN365_FINANCIALS_TEAM_MEMBERS_SKU" = "Dynamics 365 for Team Members Business Edition" 307 | "FLOW_FREE" = "Microsoft Flow Free" 308 | "POWER_BI_PRO" = "Power BI Pro" 309 | "O365_BUSINESS" = "MICROSOFT 365 APPS FOR BUSINESS" 310 | "DYN365_ENTERPRISE_SALES" = "Dynamics Office 365 Enterprise Sales" 311 | "PROJECTPROFESSIONAL" = "Project Professional" 312 | "VISIOONLINE_PLAN1" = "Visio Online Plan 1" 313 | "EXCHANGEENTERPRISE" = "Exchange Online Plan 2" 314 | "DYN365_ENTERPRISE_P1_IW" = "Dynamics 365 P1 Trial for Information Workers" 315 | "DYN365_ENTERPRISE_TEAM_MEMBERS" = "Dynamics 365 For Team Members Enterprise Edition" 316 | "CRMSTANDARD" = "Microsoft Dynamics CRM Online Professional" 317 | "EXCHANGEARCHIVE_ADDON" = "Exchange Online Archiving For Exchange Online" 318 | "SPZA_IW" = "App Connect" 319 | "WINDOWS_STORE" = "Windows Store for Business" 320 | "MCOEV" = "Microsoft Phone System" 321 | "MCOEV_GOV" = "MICROSOFT 365 PHONE SYSTEM FOR GCC" 322 | "SPE_E5" = "Microsoft 365 E5" 323 | "SPE_E3" = "Microsoft 365 E3" 324 | "MCOPSTN1" = "PSTN DOMESTIC CALLING" 325 | "MCOPSTN2" = "Domestic and International Calling Plan" 326 | "MCOPSTN_" = "MICROSOFT 365 DOMESTIC CALLING PLAN (120 Minutes)" 327 | "DYN365_TEAM_MEMBERS" = "Dynamics 365 Team Members" 328 | "WIN10_PRO_ENT_SUB" = "WINDOWS 10 ENTERPRISE E3" 329 | "WIN10_VDA_E3" = "WINDOWS 10 ENTERPRISE E3" 330 | "WIN10_VDA_E5" = "Windows 10 Enterprise E5" 331 | "MDATP_XPLAT" = "Microsoft Defender for Endpoint" 332 | "CCIBOTS_PRIVPREV_VIRAL" = "Power Virtual Agents Viral Trial" 333 | "ADALLOM_STANDALONE" = "Microsoft Cloud App Security" 334 | "BUSINESS_VOICE_MED2_TELCO" = "Microsoft 365 Business Voice (US)" 335 | 336 | } 337 | 338 | 339 | 340 | 341 | ###Connect to your Own Partner Center to get a list of customers/tenantIDs ######### 342 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 343 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 344 | 345 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 346 | 347 | $customers = Get-MsolPartnerContract -All 348 | 349 | Write-Host "Found $($customers.Count) customers in Partner Center." -ForegroundColor DarkGreen 350 | 351 | 352 | foreach ($customer in $customers) { 353 | Write-Host "Found $($customer.Name) in Partner Center" -ForegroundColor Green 354 | $CustomerToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -Tenant $customer.TenantID 355 | $headers = @{ "Authorization" = "Bearer $($CustomerToken.AccessToken)" } 356 | write-host "Collecting data for $($Customer.Name)" -ForegroundColor Green 357 | $domain = $customer.DefaultDomainName 358 | $AllDomains = Get-MsolDomain -TenantId $customer.TenantID 359 | $Licenselist = (Invoke-RestMethod -Uri "https://graph.microsoft.com/beta/subscribedSkus" -Headers $Headers -Method Get -ContentType "application/json").value 360 | $Licenselist | ForEach-Object { $_.skupartnumber = "$($AccountSkuIdDecodeData.$($_.skupartnumber))" } 361 | $Users = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/beta/users?$top=999' -Headers $Headers -Method Get -ContentType "application/json").value | Select-Object DisplayName, proxyaddresses, AssignedLicenses, userprincipalname 362 | $UserObj = foreach ($user in $users) { 363 | [PSCustomObject]@{ 364 | 'DisplayName' = $user.displayname 365 | 'UPN' = $user.userprincipalname 366 | "LicensesAssigned" = ($Licenselist | Where-Object { $_.skuid -in $User.assignedLicenses.skuid }).skupartnumber -join "`n" 367 | } 368 | 369 | } 370 | $licenseObj = foreach ($License in $Licenselist) { 371 | [PSCustomObject]@{ 372 | 'License Name' = $license.skupartnumber 373 | 'Active Licenses' = $license.prepaidUnits.enabled - $license.prepaidUnits.suspended 374 | 'Consumed Licenses' = $license.consumedunits 375 | 'unused licenses' = $license.prepaidUnits.enabled - $license.prepaidUnits.suspended - $license.consumedunits 376 | } 377 | } 378 | $customer_id = ($CustomerObj | Where-Object { $_.Domain -in $AllDomains.name}).customer_id 379 | $page = 1 380 | $totaldocCount = (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 381 | $CurrentDocuments = Do{ 382 | (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).wiki_pages 383 | $page = $page + 1 384 | }Until ($page -gt $totaldocCount) 385 | 386 | $name = "Microsoft License Report: $($customer.Name)" 387 | if($customer_id){ 388 | $bodyVariables = @{ 389 | "customer_Name" = $customer.Name 390 | "Tenant_ID" = $customer.TenantID 391 | "licenses" = ($licenseObj | select-object 'License Name', 'Active licenses', 'Consumed Licenses', 'Unused Licenses' | convertto-html -Fragment | out-string) 392 | "licensed_users" = (($UserObj | Where-Object { $_.'LicensesAssigned' -ne "" }) | convertto-html -Fragment | out-string) 393 | "unlicensed_users" = (($UserObj | Where-Object { $_.'LicensesAssigned' -eq "" }) | convertto-html -Fragment | out-string) 394 | 395 | } 396 | $body = "

Customer Name: $($bodyVariables.customer_Name)

397 | 398 |

TenantID: $($bodyVariables.Tenant_ID)

399 | 400 |

License Summary: $($bodyVariables.licenses)

401 | 402 |

Licensed Users: $($bodyVariables.licensed_users)

403 | 404 |

Unlicensed Users: $($bodyVariables.unlicensed_users)

" 405 | 406 | $docExist = ($CurrentDocuments | where-object {$_.name -eq $name}) 407 | 408 | if(!$docExist){ 409 | Write-Host "Creating a new document" -ForegroundColor Green 410 | Create-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerID $customer_id -name $name -body $body 411 | } else { 412 | Write-Host "Document already exist. Updating Existing Document" -ForegroundColor Yellow 413 | Update-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -WikiID $docExist.id -body $body 414 | } 415 | 416 | } 417 | } 418 | -------------------------------------------------------------------------------- /Microsoft_Syncro_MFAStatus.ps1: -------------------------------------------------------------------------------- 1 | Param 2 | ( 3 | 4 | [cmdletbinding()] 5 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationId from the Secure Application Model https://github.com/KelvinTegelaar/SecureAppModel/blob/master/Create-SecureAppModel.ps1")] 6 | [string]$ApplicationId, 7 | [Parameter(Mandatory= $true, HelpMessage="Enter your ApplicationSecret from the Secure Application Model")] 8 | [string]$ApplicationSecret, 9 | [Parameter(Mandatory= $true, HelpMessage="Enter your Partner Tenantid")] 10 | [string]$tenantID, 11 | [Parameter(Mandatory= $true, HelpMessage="Enter your refreshToken from the Secure Application Model")] 12 | [string]$refreshToken, 13 | [Parameter(Mandatory= $true)] 14 | [string]$SyncroAPIKey, 15 | [Parameter(Mandatory= $true)] 16 | [string]$SyncroSubdomain 17 | 18 | ) 19 | 20 | # Check if the MSOnline PowerShell module has already been loaded. 21 | if ( ! ( Get-Module MSOnline) ) { 22 | # Check if the MSOnline PowerShell module is installed. 23 | if ( Get-Module -ListAvailable -Name MSOnline ) { 24 | Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..." 25 | Import-Module MsOnline 26 | } else { 27 | Install-Module MsOnline 28 | } 29 | } 30 | 31 | ###MICROSOFT SECRETS##### 32 | 33 | $ApplicationId = $ApplicationId 34 | $ApplicationSecret = $ApplicationSecret 35 | $tenantID = $tenantID 36 | $refreshToken = $refreshToken 37 | $secPas = $ApplicationSecret| ConvertTo-SecureString -AsPlainText -Force 38 | $credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $secPas) 39 | 40 | 41 | ###Syncro Secrets#### 42 | 43 | $SyncroSubdomain = $SyncroSubdomain 44 | $SyncroAPIKey = $SyncroAPIKey 45 | 46 | 47 | ###API Permissions Needed In Syncro##### 48 | # Customers - List/Search 49 | # Customers - View Detail 50 | # Customers - Edit 51 | # Documentation - Allow Usage 52 | # Documentation - Create 53 | # Documentation - Edit 54 | 55 | 56 | 57 | ###FUNCTION TO Get All Customers IN SYNCRO#### 58 | 59 | function GetAll-Customers () { 60 | 61 | <# 62 | .SYNOPSIS 63 | This function is used to get all customer records in Syncro. 64 | .DESCRIPTION 65 | The function connects to your Syncro environment and finds all customers 66 | .EXAMPLE 67 | GetAll-Customers -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 68 | Retrieves all customers 69 | .NOTES 70 | NAME: GetAll-Customers 71 | #> 72 | 73 | [cmdletbinding()] 74 | 75 | param 76 | ( 77 | [Parameter(Mandatory=$true)] 78 | [string]$SyncroSubdomain, 79 | [string]$SyncroAPIKey, 80 | [string]$page 81 | ) 82 | 83 | 84 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/customers?api_key=$($SyncroAPIKey)&page=$($page)" 85 | $response = Invoke-RestMethod -Uri $url -Method Get -ContentType 'application/json' 86 | $response 87 | 88 | } 89 | 90 | 91 | 92 | ###Update Documents in Syncro ####### 93 | 94 | function Update-WikiPage () { 95 | 96 | <# 97 | .SYNOPSIS 98 | This function is used to update a document in Syncro. 99 | .DESCRIPTION 100 | The function connects to your Syncro environment and updates a document 101 | .EXAMPLE 102 | Update-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -WikiID $WikiID -body $body 103 | Updates document with new body. 104 | .NOTES 105 | NAME: Update-WikiPage 106 | #> 107 | 108 | [cmdletbinding()] 109 | 110 | param 111 | ( 112 | [Parameter(Mandatory=$true)] 113 | [string]$SyncroSubdomain, 114 | [string]$SyncroAPIKey, 115 | [string]$WikiID, 116 | [string]$body 117 | ) 118 | 119 | 120 | 121 | $UpdateWikiPage =@{ 122 | body = $body 123 | api_key=$SyncroAPIKey 124 | } 125 | 126 | $body = (ConvertTo-Json $UpdateWikiPage) 127 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages/$($WikiID)" 128 | $response = Invoke-RestMethod -Uri $url -Method Put -Body $body -ContentType 'application/json' 129 | $response 130 | 131 | } 132 | 133 | ###Create new document in Syncro ####### 134 | 135 | function Create-WikiPage () { 136 | 137 | <# 138 | .SYNOPSIS 139 | This function is used to create a document for a customer in Syncro. 140 | .DESCRIPTION 141 | The function connects to your Syncro environment and creates a document for a customer 142 | .EXAMPLE 143 | Create-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey -customerID $customerID -name $name -body $body 144 | Creates a new document for a customer in Syncro 145 | .NOTES 146 | NAME: Create-WikiPage 147 | #> 148 | 149 | [cmdletbinding()] 150 | 151 | param 152 | ( 153 | [Parameter(Mandatory=$true)] 154 | [string]$SyncroSubdomain, 155 | [string]$SyncroAPIKey, 156 | [string]$customerID, 157 | [string]$name, 158 | $body 159 | ) 160 | 161 | 162 | 163 | $CreatePage =@{ 164 | customer_id = $customerID 165 | name = $name 166 | body= $body 167 | api_key=$SyncroAPIKey 168 | } 169 | 170 | $body = (ConvertTo-Json $CreatePage) 171 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages" 172 | $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType 'application/json' 173 | $response 174 | 175 | } 176 | 177 | ###GET All Documentsin Syncro ####### 178 | 179 | function Get-WikiPage () { 180 | 181 | <# 182 | .SYNOPSIS 183 | This function is used to get all documents within Syncro 184 | .DESCRIPTION 185 | The function connects to your Syncro environment and gets all documents 186 | .EXAMPLE 187 | Get-WikiPage -SyncroSubDomain $SyncroSubDomain -SyncroAPIKey $SyncroAPIkey 188 | Gets all documents that exist 189 | .NOTES 190 | NAME: Get-WikiPage 191 | #> 192 | 193 | [cmdletbinding()] 194 | 195 | param 196 | ( 197 | [Parameter(Mandatory=$true)] 198 | [string]$SyncroSubdomain, 199 | [string]$SyncroAPIKey 200 | ) 201 | 202 | 203 | $url = "https://$($SyncroSubdomain).syncromsp.com/api/v1/wiki_pages?api_key=$($SyncroAPIKey)" 204 | $response = Invoke-RestMethod -Uri $url -Method GET -ContentType 'application/json' 205 | $response 206 | 207 | } 208 | 209 | 210 | 211 | ###Fnd All Syncro Customers########## 212 | Write-Host "Getting All Customers In Syncro" 213 | 214 | $page = 1 215 | $totalPageCount = (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 216 | $SyncroCustomers = Do{ 217 | (GetAll-Customers -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).customers 218 | $page = $page + 1 219 | }Until ($page -gt $totalPageCount) 220 | Write-Host "Found $($SyncroCustomers.Count) Customers in Syncro" -ForegroundColor Green 221 | $CustomerObj = forEach ($customer in $SyncroCustomers) { 222 | Write-Host "Getting domain for $($customer.business_name)" 223 | $customerDomain = ($customer.email -split "@")[1] 224 | if(!$customerDomain){ 225 | Write-Host "$($customer.business_name) does not have an email on file" -ForegroundColor Red 226 | } else { 227 | Write-Host "Customer domain is $($customerDomain)" 228 | } 229 | [PSCustomObject]@{ 230 | Domain = $customerDomain 231 | customer_id = $customer.id 232 | } 233 | 234 | } 235 | 236 | ###Connect to your Own Partner Center to get a list of customers/tenantIDs ######### 237 | $aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.windows.net/.default' -ServicePrincipal -Tenant $tenantID 238 | $graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -ServicePrincipal -Tenant $tenantID 239 | 240 | 241 | Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken 242 | 243 | $customers = Get-MsolPartnerContract -All 244 | 245 | Write-Host "Found $($customers.Count) customers in Partner Center." -ForegroundColor DarkGreen 246 | 247 | 248 | foreach ($customer in $customers) { 249 | Write-Host "Found $($customer.Name) in Partner Center" -ForegroundColor Green 250 | $CustomerToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes 'https://graph.microsoft.com/.default' -Tenant $customer.TenantID 251 | $headers = @{ "Authorization" = "Bearer $($CustomerToken.AccessToken)" } 252 | $domain = $customer.DefaultDomainName 253 | $AllDomains = Get-MsolDomain -TenantId $customer.TenantID 254 | $Users = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/users?$top=999' -Headers $Headers -Method Get -ContentType "application/json").value | Select-Object DisplayName, proxyaddresses, AssignedLicenses, userprincipalname 255 | $MFAStatus = Get-MsolUser -all -TenantId $customer.TenantId | Select-Object DisplayName,UserPrincipalName,@{N="MFA Status"; E={if( $_.StrongAuthenticationRequirements.State -ne $null) {$_.StrongAuthenticationRequirements.State} else { "Disabled"}}} 256 | 257 | 258 | write-host "Grabbing Potential Conditional Access MFA Registration for $($Customer.name)" -ForegroundColor Green 259 | try{ 260 | $uri = "https://graph.microsoft.com/beta/reports/credentialUserRegistrationDetails" 261 | $MFA2 = (Invoke-RestMethod -Uri $uri -Headers $headers -Method Get).value | Select-Object userPrincipalName, isMfaRegistered, @{N="MFA Registration Status"; E={if( $_.isMfaRegistered -ne $null) {$_.isMfaRegistered } else { "Disabled"}}} 262 | } catch { 263 | Write-Host "$($customer.name) does not have Azure AD P1 licensing" 264 | } 265 | 266 | write-host "Grabbing Conditional Access Policies $($Customer.name)" -ForegroundColor Green 267 | try{ 268 | $CAPolicy = (Invoke-RestMethod -Uri 'https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$count=true' -Headers $Headers -Method Get -ContentType "application/json").value| Select-Object count, grantControls, displayName 269 | $CAPolicy2 = $CAPolicy.grantControls 270 | $duoMFA = $CAPolicy2. customAuthenticationFactors 271 | $customer | Add-Member ConditionalAccessPolicies $CAPolicy.count 272 | $customer | Add-Member CustomControl $duoMFA 273 | } catch { 274 | Write-Host "$($customer.name) does not have Azure AD P1 licensing" 275 | } 276 | 277 | write-host "Grabbing Security Defaults Policy for $($Customer.name)" -ForegroundColor Green 278 | $uri = "https://graph.microsoft.com/v1.0/policies/identitySecurityDefaultsEnforcementPolicy" 279 | $Data2 = Invoke-RestMethod -Uri $uri -Headers $Headers -Method Get | Select-Object isEnabled 280 | $customer | Add-Member SecurityDefaultsEnabled $Data2.isEnabled 281 | 282 | $UserObj = foreach ($user in $users) { 283 | [PSCustomObject]@{ 284 | 'Display name' = $user.displayname 285 | "Legacy MFA Enabled" = ($MFAStatus | Where-Object { $_.UserPrincipalName -eq $user.userPrincipalName}).'MFA Status' 286 | "MFA Registered through Security Defaults or CA Policy" = ($MFA2 | where-object { $_.userPrincipalName -eq $user.userPrincipalName}).'MFA Registration Status' 287 | } 288 | 289 | } 290 | $tenantObj= [PSCustomObject]@{ 291 | 'Customer Name' = $customer.name 292 | 'Security Defaults Enabled' = $customer.SecurityDefaultsEnabled 293 | 'Conditional Access Policies' = $customer.ConditionalAccessPolicies 294 | } 295 | $conditionalAccessObj = foreach ($policy in $CAPolicy) { 296 | [PSCustomObject]@{ 297 | 'Conditional Access Policy' = $policy.displayname 298 | 299 | } 300 | $customObj = [PSCustomObject]@{ 301 | 'CustomControl' = $CAPolicy.grantControls.customAuthenticationFactors 302 | 303 | } 304 | } 305 | $customer_id = ($CustomerObj | Where-Object { $_.Domain -in $AllDomains.name}).customer_id 306 | $page = 1 307 | $totaldocCount = (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page 1).meta.total_pages 308 | $CurrentDocuments = Do{ 309 | (Get-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -page $page).wiki_pages 310 | $page = $page + 1 311 | }Until ($page -gt $totaldocCount) 312 | $name = "Microsoft Authentication Report: $($customer.Name)" 313 | if($customer_id){ 314 | $bodyVariables = @{ 315 | "customer_Name" = $customer.Name 316 | "Tenant_ID" = $customer.TenantID 317 | "MFA_Status" = ($UserObj | select-object 'Display name', 'Legacy MFA Enabled', 'MFA Registered through Security Defaults or CA Policy' | convertto-html -Fragment | out-string) 318 | "Conditional_Access_Policies" = ($conditionalAccessObj | select-object 'Conditional Access Policy' | convertto-html -Fragment | out-string) 319 | "security_defaults" = ($tenantObj | select-object 'Security Defaults Enabled' | convertto-html -Fragment | out-string) 320 | "Duo_MFA" = ($customObj | select-object 'CustomControl' | convertto-html -Fragment | out-string) 321 | 322 | } 323 | $body = "

Customer Name: $($bodyVariables.customer_Name)

324 | 325 |

TenantID: $($bodyVariables.Tenant_ID)

326 | 327 |

MFA Status: $($bodyVariables.MFA_Status)

328 | 329 |

Conditional Access Policies: $($bodyVariables.Conditional_Access_Policies)

330 | 331 |

Security Defaults Enabled: $($bodyVariables.security_defaults)

332 | 333 |

DUO MFA: $($bodyVariables.Duo_MFA)

" 334 | 335 | $docExist = ($CurrentDocuments | where-object {$_.name -eq $name}) 336 | 337 | if(!$docExist){ 338 | Write-Host "Creating a new document" -ForegroundColor Green 339 | Create-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -customerID $customer_id -name $name -body $body 340 | } else { 341 | Write-Host "Document already exist. Updating Existing Document" -ForegroundColor Yellow 342 | Update-WikiPage -SyncroSubdomain $SyncroSubdomain -SyncroAPIKey $SyncroAPIKey -WikiID $docExist.id -body $body 343 | } 344 | 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Syncro-Documentation 2 | 3 | Documenting customer Microsoft 365 environments can be extremely tedious and time consuming. Keeping that information up to date is also a heavy task to manage. I wanted to create some scripts that would automate and update documents in Syncro that are tied to your customer environments. For this reason I created some scripts that perform the following: 4 | 11 |
All documentation is listed for the customer and contacts are automatically added or updated to the customer record.
12 |
13 | 14 | Download SPPKG file screenshot 15 | 16 | 17 |

Prerequisites

18 | You will need to garner tokens and GUIDs from both the Secure Application Model and Syncro. The secure application model allows for a headless connection into all of your customer environments. The script to run that can be found from Kelvin over at CyberDrain. Click here to go to that page in Github. 19 |

20 | In Syncro you will need to create a new API Key that has permissions to customers and documentation to perform the necessary read and write actions. Click Here for Syncro's Documentation on generating a new API key. The only other variable you will be prompted for is your Syncro subdomain. This is simply just the prefix of the URL you go to when signing in. Ex: 21 |

22 | 23 | Download SPPKG file screenshot 24 | 25 | 26 |

Author(s)

27 | Nick Ross 28 | 29 |

Microsoft License Report

30 | 31 | The license report displays all active and consumed licensing as well as all licensed and unlicensed users 32 |

33 | 34 | Download SPPKG file screenshot 35 | 36 | 37 | Download SPPKG file screenshot 38 | 39 | 40 |

Microsoft MFA Status

41 | 42 | This report shows you all users MFA status. Since Microsoft has evolved their MFA registration over time there are now 3 different ways in which a user could have MFA enforced. 43 | 48 | For this reason I include two columns that shows if a user has registered with one of these methods (I am able to see Security Defaults and Conditional Access Registration in a combined output). Here I also show the names of all Conditional Access Policies and also an additional filed that shows if you have DUO listed as a custom control in any conditional access policy 49 |

50 | 51 | Download SPPKG file screenshot 52 | 53 | 54 | Download SPPKG file screenshot 55 | 56 | 57 |

Microsoft Exchange Report

58 | For the exchange report I display mailboxes, active consumption in GB (in a descending order), last login time, mailflow rules, DKIM Configuration, and ATP Settings/Policies 59 |

60 | 61 | Download SPPKG file screenshot 62 | 63 | 64 | Download SPPKG file screenshot 65 | 66 | 67 |

Microsoft Intune Report

68 | The Intune report displays a summary of device compliance, devices, and all apps that are in an assigned state 69 |

70 | 71 | Download SPPKG file screenshot 72 | 73 | 74 |

Microsoft Contact Creation

75 | Microsoft 365 Users are created as contacts for a customer if they do not already exist. If the user is not longer in 365, then a note is added to an existing contact to say that they are no longer active. (Wish there was a flag or something for this in Syncro as it wasn't the best way to show removal of users) 76 |

77 | 78 | Download SPPKG file screenshot 79 | 80 | 81 |

License

82 | This project is under the MIT license. 83 | -------------------------------------------------------------------------------- /Syncro/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic1.png -------------------------------------------------------------------------------- /Syncro/pic10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic10.png -------------------------------------------------------------------------------- /Syncro/pic11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic11.png -------------------------------------------------------------------------------- /Syncro/pic12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic12.png -------------------------------------------------------------------------------- /Syncro/pic13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic13.png -------------------------------------------------------------------------------- /Syncro/pic14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic14.png -------------------------------------------------------------------------------- /Syncro/pic15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic15.png -------------------------------------------------------------------------------- /Syncro/pic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic2.png -------------------------------------------------------------------------------- /Syncro/pic3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic3.png -------------------------------------------------------------------------------- /Syncro/pic4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic4.1.png -------------------------------------------------------------------------------- /Syncro/pic4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic4.png -------------------------------------------------------------------------------- /Syncro/pic5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic5.png -------------------------------------------------------------------------------- /Syncro/pic6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic6.png -------------------------------------------------------------------------------- /Syncro/pic7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic7.png -------------------------------------------------------------------------------- /Syncro/pic8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic8.png -------------------------------------------------------------------------------- /Syncro/pic9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msp4msps/Syncro-Documentation/7e27c613642911cf51c655b8e364df610dbd5746/Syncro/pic9.png --------------------------------------------------------------------------------