fleet/website/api/controllers/microsoft-proxy/update-one-devices-compliance-status.js
Eric 13eeebe548
Website: Add Microsoft compliance proxy endpoints. (#27403)
Changes:
- Created a new database model: `MicrosoftComplianceTenant`. A model
that stores information about complaince tenants
- Added `/policies/is-cloud-customer`: a policy that blocks requests to
microsoft proxy endpoints if a `MS API KEY` header is missing or does
not match a new config variable
(`sails.custom.config.cloudCustomerCompliancePartnerSharedSecret`)
- Added `microsoft-proxy/create-compliance-partner-tenant`: an action
that creates a database record for a new compliance tenant and generates
an API key that is used to authenticate future requests to microsoft
proxy endpoints for an entra tenant.
- Added `microsoft-proxy/get-compliance-partner-settings`: an action
that returns information about Fleet's complaince partner entra
application and the entra tenant's admin consent status (whether or not
a tenant's entra admin has granted permissions to Fleet's compliance
partner application)
- Added `microsoft-proxy/get-tenants-admin-consent-status`: an action
that updates the admin consent status of a compliance tenant record.
- Added `microsoft-proxy/setup-compliance-partner-tenant`: an action
that provisions a compliance tenant, creates a complaince policy for
macOS devices assigns the created policy to the built-in "All users"
user group on the tenants entra instance.
- Added `microsoft-proxy/update-one-devices-compliance-status`: an
action that receives information about a device on a compliance tenant's
Fleet instance, sends that information to their Entra instance, and
returns the messsage ID returned by the asynchronus Entra API.
- Added `microsoft-proxy/get-one-compliance-status-result`: an action
that returns the result of a compliance status update from the Entra
API.
- Added `sails.helpers.microsoft-proxy.get-access-token-and-api-urls` A
helper that gets an access token for a tenant's entra instance and the
URLs of the API endpoints the microsoft proxy actions use for a tenant.
- Added `scripts/send-entra-heartbeat-requests` A script that will run
daily to keep all microsoft compliance integrations provisioned.
-

---------

Co-authored-by: Lucas Rodriguez <[email protected]>
2025-06-11 13:01:36 -05:00

149 lines
5.1 KiB
JavaScript
Vendored

module.exports = {
friendlyName: 'Receive one devices compliance status',
description: 'Receives compliance information about a device and sends it to a tenant\'s Entra instance for processing.',
inputs: {
entraTenantId: {
type: 'string',
required: true,
},
fleetServerSecret: {
type: 'string',
requried: true,
},
deviceId: {
type: 'string',
description: 'The devices ID in entra',
required: true,
},
deviceManagementState: {
type: 'boolean',
description: 'Whether or not this device is enrolled in an MDM.',
required: true,
},
deviceName: {
type: 'string',
description: 'The device\'s display name in Fleet.',
required: true,
},
os: {
type: 'string',
required: true,
},
osVersion: {
type: 'string',
required: true,
},
userPrincipalName: {
type: 'string',
required: true,
},
compliant: {
type: 'boolean',
required: true,
},
lastCheckInTime: {
type: 'number',
description: 'The device\'s last sync with Fleet in Unix seconds',
required: true,
},
},
exits: {
success: { description: 'A devices compliance status was successfully sent to an entra tenants instance'},
},
fn: async function ({entraTenantId, fleetServerSecret, deviceId, deviceManagementState, deviceName, os, osVersion, userPrincipalName, compliant, lastCheckInTime}) {
let informationAboutThisTenant = await MicrosoftComplianceTenant.findOne({entraTenantId: entraTenantId, fleetServerSecret: fleetServerSecret});
if(!informationAboutThisTenant) {
return new Error({error: 'No MicrosoftComplianceTenant record was found that matches the provided entra_tenant_id and fleet_server_secret combination.'});
}
let tokenAndApiUrls = await sails.helpers.microsoftProxy.getAccessTokenAndApiUrls.with({
complianceTenantRecordId: informationAboutThisTenant.id
});
let graphAccessToken = tokenAndApiUrls.graphAccessToken;
let accessToken = tokenAndApiUrls.manageApiAccessToken;
let deviceDataSyncUrl = tokenAndApiUrls.deviceDataSyncUrl;
// [?]: https://learn.microsoft.com/en-us/graph/api/resources/users
// Get the GUID for this user using the UserPrincipalName
let informationAboutThisUser = await sails.helpers.http.get.with({
url: `https://graph.microsoft.com/v1.0/users('${userPrincipalName}')`,
headers: {
'Authorization': `Bearer ${graphAccessToken}`
}
}).intercept((err)=>{
return new Error({error: `An error occurred when getting a user ID from a user principal name (${userPrincipalName}) for a complaince status update. Full error: ${require('util').inspect(err, {depth: 3})}`});
});
if(!informationAboutThisUser.id) {
return new Error({error: `An error occurred when getting information about a user (${userPrincipalName}). The response from the Microsoft graph API did not include an ID.`});
}
let lastUpdateTime = new Date().toISOString();
// Build the compliance report for this device:
let complianceUpdateContent = [
{
EntityType: 1, // EntityType 1 = Device inventory data.
TenantId: informationAboutThisTenant.entraTenantId,
DeviceManagementState: deviceManagementState ? 'managed' : 'notManaged',
DeviceId: deviceId,
DeviceName: deviceName,
UserId: informationAboutThisUser.id,
// LastCheckInTime is a global timestamp indicating the time of device sync with partner service.
LastCheckInTime: new Date(lastCheckInTime * 1000).toISOString(),
// LastUpdateTime is a global timestamp indicating the order of messages.
LastUpdateTime: lastUpdateTime,
Os: os,
OsVersion: osVersion,
EasIds: [],// This field is required but can be sent as an empty array.
State: 0,
},
{
EntityType: 4, // EntityType 4 = compliance data
TenantId: informationAboutThisTenant.entraTenantId,
DeviceId: deviceId,
UserId: informationAboutThisUser.id,
// LastUpdateTime is a global timestamp indicating the order of messages.
LastUpdateTime: lastUpdateTime,
complianceStatus: compliant ? 'compliant' : 'notCompliant',
}
];
// Generate a UUID for this compliance update.
let messageId = sails.helpers.strings.uuid();
await sails.helpers.http.sendHttpRequest.with({
method: 'PUT',
url: `${deviceDataSyncUrl}/DataUploadMessages(guid'${encodeURIComponent(messageId)}')?api-version=1.2`,
headers: {
'Authorization': `Bearer ${accessToken}`
},
body: {
TenantId: informationAboutThisTenant.entraTenantId,
UploadTime: new Date().toISOString(),
Content: JSON.stringify(complianceUpdateContent),
}
}).intercept((err)=>{
return new Error({error: `An error occurred when sending a request to sync a device's compliance status for a Microsoft compliance tenant. Full error: ${require('util').inspect(err, {depth: 3})}`});
});
return {
message_id: messageId,// eslint-disable-line camelcase
};
}
};