Link the Entra ids on a PC to the Azure portal
The Entra Device Info card showed two GUIDs and nothing to do with them.
Copying one into the portal by hand is the kind of errand a detail page exists
to remove.
Each link sits BESIDE the id it is built from rather than in a row at the top
of the card, because the pairing is counterintuitive: the DEVICE id opens
Intune and the OBJECT id opens Entra. A separate action row would have to
re-label which is which, and would still leave the reader matching them up.
BOTH URLS ARE SETTINGS WITH AN EMPTY DEFAULT, and that is the whole design.
The host is a tenant fact, not a product one - this site is on the US
Government cloud, portal.azure.us, where a commercial-cloud default would send
people to a page that does not exist. Microsoft also moves these blade paths,
and a site that hits that can fix it by editing a setting rather than waiting
for a release. Empty means no link and the id still renders as text, so a site
that configures nothing is unchanged. The example values live in each
setting's description, per cloud.
Same shape as the ServiceNow URL templates already in integrations, down to
the {token} placeholder, so there is one idiom for "deep link into a system we
do not own" rather than two.
Scope: product
This commit is contained in:
@@ -196,13 +196,35 @@
|
||||
>
|
||||
<h3 class="section-title">Entra Device Info</h3>
|
||||
<div class="info-list">
|
||||
<!-- The link sits beside the id it is built from, not in a row
|
||||
of its own at the top: the pairing is counterintuitive - the
|
||||
DEVICE id opens Intune and the OBJECT id opens Entra - and a
|
||||
separate action row would have to re-label which is which. -->
|
||||
<div v-if="computer.computer?.azuredeviceid" class="info-row">
|
||||
<span class="info-label">Device ID</span>
|
||||
<span class="info-value">{{ computer.computer.azuredeviceid }}</span>
|
||||
<span class="info-value">
|
||||
{{ computer.computer.azuredeviceid }}
|
||||
<a
|
||||
v-if="intuneDeviceLink"
|
||||
:href="intuneDeviceLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="portal-link"
|
||||
>Intune</a>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="computer.computer?.azureobjectid" class="info-row">
|
||||
<span class="info-label">Object ID</span>
|
||||
<span class="info-value">{{ computer.computer.azureobjectid }}</span>
|
||||
<span class="info-value">
|
||||
{{ computer.computer.azureobjectid }}
|
||||
<a
|
||||
v-if="entraDeviceLink"
|
||||
:href="entraDeviceLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="portal-link"
|
||||
>Entra</a>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="computer.computer?.azuredevicecategory" class="info-row">
|
||||
<span class="info-label">Device Category</span>
|
||||
@@ -342,6 +364,7 @@ import PluginAssetPanels from '@/components/PluginAssetPanels.vue'
|
||||
import AssetDetailTabs from '@/components/AssetDetailTabs.vue'
|
||||
import AssetRelationships from '@/components/AssetRelationships.vue'
|
||||
import { useIdentifierFlags } from '@/composables/identifierSettings'
|
||||
import { getEntraDeviceUrls } from '@/utils/siteSettings'
|
||||
import { setRecordTitle } from '@/composables/pageTitle'
|
||||
import { useToast } from '@/composables/toast'
|
||||
import { apiError } from '@/utils/apiError'
|
||||
@@ -374,6 +397,22 @@ const assetPanels = ref(null)
|
||||
// which is the answer somebody arriving from a search or a link came for.
|
||||
const activegroup = ref('details')
|
||||
|
||||
// Azure portal deep links, built from templates a site configures. Empty
|
||||
// templates mean no link at all, which is the default: the host differs per
|
||||
// tenant (this site is on the US Government cloud) and Microsoft moves these
|
||||
// blade paths, so guessing one would send people to a dead page.
|
||||
const entraUrlTemplate = ref('')
|
||||
const intuneUrlTemplate = ref('')
|
||||
|
||||
function fillTemplate(template, token, value) {
|
||||
if (!template || !value) return null
|
||||
return template.split(token).join(encodeURIComponent(value))
|
||||
}
|
||||
const entraDeviceLink = computed(() => fillTemplate(
|
||||
entraUrlTemplate.value, '{objectid}', computer.value?.computer?.azureobjectid))
|
||||
const intuneDeviceLink = computed(() => fillTemplate(
|
||||
intuneUrlTemplate.value, '{deviceid}', computer.value?.computer?.azuredeviceid))
|
||||
|
||||
// Only offer a tab that has something in it. An empty tab is worse than an
|
||||
// absent one - it reads as broken rather than as "this site does not collect
|
||||
// that". Details always shows because core fills it regardless of plugins.
|
||||
@@ -451,6 +490,13 @@ function relativeAge(value) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Non-critical: a site that has not configured these just gets no link, so a
|
||||
// failure here must not stop the page loading.
|
||||
getEntraDeviceUrls().then(({ entraUrl, intuneUrl }) => {
|
||||
entraUrlTemplate.value = entraUrl
|
||||
intuneUrlTemplate.value = intuneUrl
|
||||
}).catch(() => {})
|
||||
|
||||
try {
|
||||
const response = await computersApi.get(route.params.id)
|
||||
computer.value = response.data.data
|
||||
@@ -606,6 +652,18 @@ async function confirmRetire() {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.portal-link {
|
||||
font-size: 0.78rem;
|
||||
margin-left: 0.5rem;
|
||||
padding: 0.05rem 0.4rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
color: var(--link, var(--primary));
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.portal-link:hover { border-color: var(--primary); }
|
||||
|
||||
|
||||
/* PC-specific styles - shared styles are in global style.css */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user