Lock and Unlock Service
In this guide, we will create a self-service action in Port that uses the upsert entity backend to lock and unlock a service.
The locking and unlocking mechanism involves
updating the locked
property in Port on a production
or development
environment.
Additionally, the action will send a Slack notification to a designated channel.
Use cases
- Maintenance & Critical Events: Lock deployment during maintenance, peak traffic, or critical events to maintain stability.
- Emergency Situations: Lock deployment in emergencies such as security breaches or vulnerabilities to mitigate risks and prevent further issues.
- Post-Incident Fixes: Unlock deployment to allow teams to implement necessary fixes or updates swiftly and restore system functionality.
Prerequisites
-
Install Port's GitHub app by clicking here.
The
service
blueprint that was created for you as part of the onboarding process will need to be extended with additional properties. Below, you can find the JSON definition for the blueprint with the required properties.
You can add these properties manually in the Port UI or use the JSON below to replace the existing blueprint.Service blueprint (click to expand)
{
"identifier": "service",
"title": "Service",
"icon": "Github",
"schema": {
"properties": {
"readme": {
"title": "README",
"type": "string",
"format": "markdown",
"icon": "Book"
},
"url": {
"title": "URL",
"format": "url",
"type": "string",
"icon": "Link"
},
"language": {
"icon": "Git",
"type": "string",
"title": "Language",
"enum": [
"GO",
"Python",
"Node",
"React"
],
"enumColors": {
"GO": "red",
"Python": "green",
"Node": "blue",
"React": "yellow"
}
},
"slack": {
"icon": "Slack",
"type": "string",
"title": "Slack",
"format": "url"
},
"code_owners": {
"title": "Code owners",
"description": "This service's code owners",
"type": "string",
"icon": "TwoUsers"
},
"type": {
"title": "Type",
"description": "This service's type",
"type": "string",
"enum": [
"Backend",
"Frontend",
"Library"
],
"enumColors": {
"Backend": "purple",
"Frontend": "pink",
"Library": "green"
},
"icon": "DefaultProperty"
},
"lifecycle": {
"title": "Lifecycle",
"type": "string",
"enum": [
"Production",
"Staging",
"Development"
],
"enumColors": {
"Production": "green",
"Staging": "yellow",
"Development": "blue"
},
"icon": "DefaultProperty"
},
"locked_in_prod": {
"icon": "DefaultProperty",
"title": "Locked in Prod",
"type": "boolean",
"default": false
},
"locked_reason_prod": {
"icon": "DefaultProperty",
"title": "Locked Reason Prod",
"type": "string"
},
"locked_in_test": {
"icon": "DefaultProperty",
"title": "Locked in Test",
"type": "boolean",
"default": false
},
"locked_reason_test": {
"icon": "DefaultProperty",
"title": "Locked Reason Test",
"type": "string"
},
"trigger_type": {
"icon": "DefaultProperty",
"title": "Lock or Unlock",
"type": "string"
},
"triggered_environment": {
"icon": "DefaultProperty",
"title": "Triggered Environment",
"type": "string"
}
},
"required": []
},
"mirrorProperties": {},
"calculationProperties": {},
"aggregationProperties": {},
"relations": {}
}Lock propertyOur Service blueprint has a boolean property called
locked_in_prod
. When theUPDATE_ENTITY
backend is triggered, we will update the value of this field totrue
if locking andfalse
if unlocking. Thelocked_reason_prod
property will be updated to reflect the reason for the lock or unlock. The same logic applies to the development environment (locked_in_test
).Additionally, the
trigger_type
property will be set to either "Locked" or "Unlocked" based on the action, and thetriggered_environment
property will be set to indicate which environment (Production or Test) was affected. -
Create a Slack Webhook:
- To create a Slack webhook URL, follow the steps in the Slack Incoming Webhooks Guide.
Create Service Action
Follow these steps to get started:
- Create two Port actions in the self-service page on the
Service
blueprint with the following JSON definitions:
Port Action - Lock Service (click to expand)
{
"identifier": "lock_service",
"title": "Lock Service",
"icon": "Lock",
"description": "Lock service in Port",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {
"reason": {
"type": "string",
"title": "Reason"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": [
"Production",
"Staging",
"Development"
],
"enumColors": {
"Production": "green",
"Staging": "orange",
"Development": "blue"
}
}
},
"required": [],
"order": [
"reason"
]
},
"blueprintIdentifier": "service"
},
"invocationMethod": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .entity.identifier }}",
"title": "{{ .entity.title }}",
"properties": {
"{{ if .inputs.environment == 'Production' then 'locked_in_prod' else 'locked_in_test' end }}": true,
"{{ if .inputs.environment == 'Production' then 'locked_reason_prod' else 'locked_reason_test' end }}": "{{ .inputs.reason }}",
"trigger_type": "Locked",
"triggered_environment": "{{ .inputs.environment }}"
}
}
},
"requiredApproval": false
}
Port Action - Unlock Service (click to expand)
{
"identifier": "unlock_service",
"title": "Unlock Service",
"icon": "Unlock",
"description": "Unlock service in Port",
"trigger": {
"type": "self-service",
"operation": "DAY-2",
"userInputs": {
"properties": {
"reason": {
"type": "string",
"title": "Reason"
},
"environment": {
"type": "string",
"title": "Environment",
"enum": [
"Production",
"Staging",
"Development"
],
"enumColors": {
"Production": "green",
"Staging": "orange",
"Development": "blue"
}
}
},
"required": [],
"order": [
"reason",
"environment"
]
},
"blueprintIdentifier": "service"
},
"invocationMethod": {
"type": "UPSERT_ENTITY",
"blueprintIdentifier": "service",
"mapping": {
"identifier": "{{ .entity.identifier }}",
"title": "{{ .entity.title }}",
"properties": {
"{{ if .inputs.environment == 'Production' then 'locked_in_prod' else 'locked_in_test' end }}": false,
"{{ if .inputs.environment == 'Production' then 'locked_reason_prod' else 'locked_reason_test' end }}": "{{ .inputs.reason }}",
"trigger_type": "Unlocked",
"triggered_environment": "{{ .inputs.environment }}"
}
}
},
"requiredApproval": false
}
Automation for Slack Notification
Create an automation that listens to changes in the locked
field of the entity and makes a WEBHOOK
backend request to send a Slack message.
Replace <Your Generated Slack Webhook>
in the automation definition with your actual Slack webhook URL.
Automation - Notify Slack on Service Lock Status (click to expand)
{
"identifier": "serviceLockStatusChange",
"title": "Notify Slack on Service Lock Status Change",
"icon": "Slack",
"description": "Sends a Slack message when the service lock status changes.",
"trigger": {
"type": "automation",
"event": {
"type": "ENTITY_UPDATED",
"blueprintIdentifier": "service"
},
"condition": {
"type": "JQ",
"expressions": [
".diff.after.properties.locked_in_prod != .diff.before.properties.locked_in_prod or .diff.after.properties.locked_in_test != .diff.before.properties.locked_in_test"
],
"combinator": "or"
}
},
"invocationMethod": {
"type": "WEBHOOK",
"url": "<Your Generated Slack Webhook>",
"agent": false,
"synchronized": true,
"body": {
"text": "*Port Service {{ .event.diff.after.properties.trigger_type }}*\n\n*Service Name*: {{ .event.diff.after.title }}\n*Link*: https://app.getport.io/{{ .event.context.blueprintIdentifier }}Entity?identifier={{ .event.context.entityIdentifier }}\n\n*Environment:* {{ .event.diff.after.properties.triggered_environment }}\n*Reason:* {{ if .event.diff.after.properties.triggered_environment == 'Production' then .event.diff.after.properties.locked_reason_prod else .event.diff.after.properties.locked_reason_test end }}"
}
},
"publish": true
}
Test the lock and unlock actions
Below is the result of a successful service lock and unlock alert sent to a Slack channel after triggering the action in the self-service page of your Port application: