├── 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 |