Migration Guide
Migrating from other licensing platforms to elm, and upgrading existing elm integrations to the current client API (v3.4)
Common Migration Scenarios
Migrating from other licensing platforms to elm? This guide covers the most common migration scenarios and provides practical steps for transitioning your existing licensing implementation. Already on elm and moving to a newer SDK or client API version? See Upgrading elm.
Code samples use the .NET SDK (evoleap.Licensing), which is the reference
implementation; the Java, Python, JavaScript/TypeScript, C++ and Ruby SDKs follow the same model
with language-appropriate naming.
FlexNet Publisher/FNP
Traditional concurrent licensing with floating licenses and network-based license servers.
- License server-based architecture
- Concurrent user management
- Feature-based licensing
- Complex network configuration
10Duke Enterprise
Cloud-based licensing with JWT tokens and modern API-first architecture.
- OAuth2 token authentication
- RESTful API integration
- Entitlement-based licensing
- Cloud-native design
Zentitle/Nalpeiron
Software protection with licensing enforcement and anti-piracy features.
- Embedded licensing engines
- Hardware fingerprinting
- Activation-based licensing
- Protection and obfuscation
Custom Solutions
In-house licensing systems, simple key validation, or database-driven approaches.
- Database-driven validation
- Simple key verification
- Custom restriction logic
- Basic user management
Migration Planning Checklist
Thorough planning is essential for a successful migration. Use this checklist to ensure all aspects are considered.
1. Assessment Phase
- Current Architecture: Document existing licensing integration points
- License Models: Identify current restriction types (named user, concurrent, node-locked)
- Feature Control: Catalog how features are currently enabled/disabled
- Customer Data: Inventory existing customer licenses and entitlements
- Integration Points: Map CRM, billing, and support system integrations
- Usage Patterns: Analyze current usage metrics and peak loads
- Dependencies: Identify systems that depend on current licensing
2. elm Product Configuration
- Product Type: Choose Desktop, Server, or Web API based on deployment model
- Restriction Mapping: Map existing license types to elm restriction models
- Feature Migration: Define features and components in elm portal
- RSA Key Generation: Generate public/private key pairs for signature verification
- Test Environment: Set up staging environment for testing
- License Templates: Create license templates matching current offerings
3. SDK Integration
- Code Integration: Replace existing licensing SDK calls with elm SDK
- State Management: Implement elm state persistence patterns
- Error Handling: Update error handling for elm-specific scenarios
- Testing: Comprehensive testing with various license scenarios
- Backwards Compatibility: Plan transition period for existing customers
- Performance Testing: Validate performance with elm integration
Migrating from FlexNet Publisher (FNP)
FlexNet Publisher users typically need to migrate from license server-based architecture to elm's cloud-native approach.
Architecture Comparison
FlexNet Publisher Architecture
- License server (lmgrd) deployment
- Network-based license checkout
- Vendor daemon configuration
- License file management
- Firewall and network setup
elm Architecture
- Cloud-based licensing service
- HTTPS API communication
- No server installation required
- License key-based registration
- Simplified network requirements
Code Migration Example
FlexNet Publisher Code
// FlexNet checkout
checkout_status = lc_checkout(lm_job, "FEATURE_NAME", "1.0", 1,
LM_CO_NOWAIT, &lm_handle, context);
if (checkout_status != 0) {
// Handle checkout failure
char error_msg[MAX_ERROR_LEN];
lc_errstring(lm_job, error_msg, MAX_ERROR_LEN);
fprintf(stderr, "Checkout failed: %s\n", error_msg);
return -1;
}
// Use the feature
perform_licensed_operation();
// Check in when done
lc_checkin(lm_handle, LM_CI_ALL_FEATURES, 0);
elm Equivalent
// elm component checkout
var result = await controlManager.CheckOutComponentsAsync(new[] { "FeatureName" });
if (result.Success) {
try {
// Use the feature
PerformLicensedOperation();
} finally {
// Always check in
await controlManager.CheckInComponentsAsync(new[] { "FeatureName" });
}
} else {
// Handle checkout failure with structured error handling
switch (result.FailureReason) {
case ComponentCheckoutFailureReason.InsufficientSessions:
ShowUserMessage("Feature is currently in use by maximum number of users");
break;
case ComponentCheckoutFailureReason.InsufficientTokens:
ShowUpgradeDialog("Additional licenses required for this feature");
break;
default:
LogError($"Checkout failed: {result.FailureReason}");
break;
}
}
Key Migration Benefits
Advantages of Moving to elm:
- No Infrastructure: Eliminate license server deployment and maintenance
- Hosted Service: evoleap operates the licensing service instead of you managing license servers
- Modern APIs: HTTPS JSON APIs with async/await patterns in the SDKs
- Better Analytics: Usage analytics and reporting in the elm portal
- Simplified Networking: Standard HTTPS (port 443) instead of custom ports
- Air-gapped Sites: Offline checkout, or elm Lite as a local license server, when there is no internet access
License Server Feature Mapping
| FlexNet Concept | elm Equivalent | Notes |
|---|---|---|
| Feature | Component or Feature | Use elm Features for simple on/off, Components for usage tracking |
| Concurrent Users | Session-based Component | elm components provide similar concurrent session control |
| Named User | Named User Restriction | Direct equivalent with better identity integration |
| License File | License Key | Simpler key-based activation instead of file management |
| Vendor Daemon | elm Cloud Service | Managed service eliminates custom daemon development |
Migrating from 10Duke Enterprise
10Duke Enterprise users will find elm familiar as both are cloud-native platforms, but elm provides simpler integration patterns.
API Comparison
10Duke Enterprise
// 10Duke JWT validation and entitlement check
var tokenRequest = new OAuth2TokenRequest {
GrantType = "client_credentials",
ClientId = "your-client-id",
ClientSecret = "your-client-secret"
};
var token = await client.RequestTokenAsync(tokenRequest);
// Check entitlements using Authorization API
var authzApi = new AuthzApi(token.AccessToken);
var decision = await authzApi.CheckPermissionAsync("read", "feature1");
if (decision.Allow) {
// User has permission to use feature1
UseFeature1();
} else {
ShowAccessDeniedMessage();
}
// Token refresh and management
if (token.IsExpired) {
token = await client.RefreshTokenAsync(token.RefreshToken);
}
elm Equivalent
// elm session validation - no token management needed
var sessionValidity = await controlManager.ValidateSessionAsync();
if (sessionValidity.IsValid) {
// IsValid stays true while elm is unreachable but the license's
// validation-failure grace period has not run out
if (sessionValidity.IsInValidationFailureGracePeriod) {
ShowOfflineWarning(sessionValidity.GracePeriodExpiration);
}
// Features granted by the license are cached in the validation state
if (controlManager.ValidationState.Features.Contains("Feature1")) {
UseFeature1();
} else {
ShowUpgradeMessage("Feature1 requires a Pro license");
}
} else {
// Handle various invalid session reasons
switch (sessionValidity.InvalidReason) {
case InvalidReason.LicenseExpired:
ShowRenewalDialog();
break;
case InvalidReason.ServiceUnreachable:
// Unreachable and the grace period has expired
ShowConnectivityError();
break;
default:
ShowLicenseErrorMessage(sessionValidity.InvalidReason);
break;
}
}
Key Differences from 10Duke
elm Simplifications:
- No OAuth2 Management: elm handles authentication internally
- Persistent State: Built-in state caching and offline support
- Hardware Binding: Native instance identity without custom implementation
- Simpler APIs: Single SDK instead of multiple API clients
- Automatic Refresh: Session management handled by the SDK
- Better Offline Support: Configurable offline checkout periods
Entitlement Model Mapping
| 10Duke Concept | elm Equivalent | Migration Notes |
|---|---|---|
| Permission | Feature | Direct mapping for basic entitlements |
| Resource Access | Component | Use components for usage-based licensing |
| JWT Token | Session State | elm manages authentication internally |
| User Context | User Identity | Similar user identification with better AD integration |
| License Pool | License Restrictions | Concurrent user limits map to elm restriction models |
Migrating from Zentitle/Nalpeiron
Zentitle users will appreciate elm's cloud-native approach while maintaining strong license security and hardware binding capabilities.
Activation Model Comparison
Zentitle/Nalpeiron
// Zentitle license activation and validation
var licenseManager = new LicenseManager();
var activationResult = licenseManager.ActivateLicense("LICENSE-KEY");
if (activationResult.IsValid) {
var entitlements = licenseManager.GetEntitlements();
if (entitlements.Contains("premium_features")) {
EnablePremiumFeatures();
}
// Check hardware binding
if (licenseManager.IsHardwareChanged()) {
var reactivationResult = licenseManager.ReactivateLicense();
if (!reactivationResult.IsValid) {
ShowHardwareMismatchError();
return;
}
}
} else {
ShowActivationFailureMessage(activationResult.ErrorCode);
}
// Periodic license validation
var validationResult = licenseManager.ValidateLicense();
if (!validationResult.IsValid) {
HandleLicenseInvalid(validationResult.Reason);
}
elm Equivalent
// elm registration and validation with automatic hardware binding
// (one-time activation; skip when ValidationState.Registered is already true)
if (!controlManager.ValidationState.Registered) {
var userInfo = new UserInfo("User Name", "user@company.com");
var registrationResult = await controlManager.RegisterAsync("LICENSE-KEY", userInfo);
if (!registrationResult.Success) {
// RegistrationResult carries a server message, not a reason code
ShowActivationFailureMessage(registrationResult.ErrorMessage);
return;
}
SaveState(controlManager.ValidationState); // persist after every call
}
// Each launch: begin a session. The hardware identity is re-checked here.
var sessionValidity = await controlManager.ValidateSessionAsync();
if (sessionValidity.IsValid) {
if (controlManager.ValidationState.Features.Contains("PremiumFeatures")) {
EnablePremiumFeatures();
}
} else {
switch (sessionValidity.InvalidReason) {
case InvalidReason.InconsistentRegistration:
ShowHardwareMismatchDialog(); // machine no longer matches its instance
break;
case InvalidReason.InstanceDisabled:
ShowInstanceDisabledMessage(); // vendor disabled this machine in the portal
break;
case InvalidReason.NoSeatsAvailable:
ShowSeatLimitMessage();
break;
default:
HandleSessionInvalid(sessionValidity.InvalidReason);
break;
}
}
Migration Advantages
Benefits of Moving to elm:
- Cloud-Native Security: Server-side validation instead of embedded engines
- Hardware Monitoring: Fuzzy hardware matching, with per-instance overrides in the portal
- Simplified Integration: Single SDK call instead of complex activation flows
- Better User Experience: Seamless license management for end users
- Server-side Changes: License changes take effect at the next session validation, without client updates
- Modern Architecture: HTTPS JSON APIs instead of embedded components
Protection Model Evolution
| Zentitle Feature | elm Approach | Security Improvement |
|---|---|---|
| Embedded License Engine | Cloud Validation | Server-side security reduces client-side attack surface |
| Hardware Fingerprinting | Instance Identity | Fuzzy matching of the hardware identity; mismatches report InvalidReason.InconsistentRegistration |
| Activation Codes | License Keys | Simpler key management with better user experience |
| Local License Storage | Application-persisted state | The SDK exposes a serializable ValidationState that you store (and protect) yourself;
clock roll-back is detected (InvalidReason.UserTamperingDetected) |
| Manual Reactivation | Portal-controlled hardware updates | Allow a one-time hardware update for an instance, or ignore hardware matching for instances on cloud VMs, from the elm portal |
Migrating from Custom Solutions
Custom licensing implementations vary widely, but elm can typically replace most functionality with improved reliability and features.
Common Custom Implementation Patterns
Database-Driven Licensing
License validation through database queries
- User credentials in database
- License status checks via SQL
- Feature flags in database tables
- Custom expiration logic
Simple Key Validation
Basic license key verification
- Algorithm-based key generation
- Checksum or hash validation
- No server-side verification
- Limited feature control
File-Based Licensing
License files with custom formats
- XML or JSON license files
- Digital signature verification
- Machine-specific binding
- Manual file distribution
Web Service Validation
Custom REST API for license checking
- HTTP-based validation calls
- Custom authentication
- Basic usage tracking
- Limited offline support
Migration from Database-Driven Systems
Custom Database Solution
// Custom database license validation
public class CustomLicenseManager {
public bool ValidateLicense(string username, string productKey) {
using (var connection = new SqlConnection(connectionString)) {
var query = @"
SELECT l.ExpirationDate, l.Features, u.IsActive
FROM Licenses l
JOIN Users u ON l.UserId = u.Id
WHERE u.Username = @username AND l.ProductKey = @productKey";
var result = connection.QueryFirstOrDefault(query,
new { username, productKey });
if (result == null || !result.IsActive)
return false;
if (result.ExpirationDate < DateTime.Now)
return false;
// Store features for later use
currentUserFeatures = result.Features.Split(',');
return true;
}
}
public bool HasFeature(string featureName) {
return currentUserFeatures?.Contains(featureName) ?? false;
}
}
elm Replacement
// elm replaces custom database with cloud service
public class ElmLicenseManager {
private readonly ControlManager controlManager;
public async Task<bool> ValidateLicenseAsync() {
var sessionValidity = await controlManager.ValidateSessionAsync();
return sessionValidity.IsValid;
}
public bool HasFeature(string featureName) {
return controlManager.ValidationState.Features.Contains(featureName);
}
public async Task<bool> RegisterUserAsync(string licenseKey, string userName, string email) {
var userInfo = new UserInfo(userName, email);
var result = await controlManager.RegisterAsync(licenseKey, userInfo);
return result.Success;
}
// elm handles server-side:
// - License expiration and seat limits
// - Feature and component entitlements
// - Usage tracking
// The SDK handles validation-failure grace periods and offline checkouts;
// your code persists controlManager.ValidationState between runs.
}
Benefits of elm vs Custom Solutions
- Reduced Development Time: Eliminate months of custom licensing development
- Production-Ready Security: Proven encryption and validation mechanisms
- Scalability: Cloud infrastructure handles any number of users
- Reliability: Hosted service plus client-side grace periods, instead of custom server maintenance
- Advanced Features: Components, offline checkout, usage analytics
- Cross-Platform Support: SDKs for all major platforms and languages
- Professional Support: Technical support from evoleap
Upgrading elm: Client API Versions
If you already use elm, upgrading is mostly a matter of taking a newer SDK. Each SDK is built against one client API version and calls only that version's endpoints. The elm server still serves every version below, so existing installations keep working while you roll out the new build.
| API version | Endpoint prefix | What it adds |
|---|---|---|
| v1 (legacy) | /register, /validate |
Instance registration and validation only |
| v2 (legacy) | /<method> |
register_instance, begin_session, extend_session,
end_session |
| v3.0 | /api/v3/auth/ |
Separate instance and user registration (register_user,
register_app, validate_instance), user and app sessions
(begin_user_session, begin_app_session),
current_release |
| v3.1 | /api/v3.1/auth/ |
Components: check_out_components, components_status |
| v3.2 | /api/v3.2/auth/ |
Offline use and tokens: check_out_license, check_in_license,
check_out_component_tokens, check_in_component_tokens,
record_offline_session, get_user_license,
check_out_consumable_component, check_out_webapi_components |
| v3.3 | /api/v3.3/auth/ |
get_license_info, check_in_components |
| v3.4 (current) | /api/v3.4/auth/ |
check_out_full_license (full-license checkout for elm Lite) |
| v3.5 Preview | /api/v3.5/auth/ |
custom_data (the license's key/value properties) on successful
begin_app_session, begin_user_session,
validate_instance, extend_session and
check_out_license responses; new validate_user endpoint. Not yet released. |
Upgrading from v2 or earlier:
- Sessions split: v2
begin_sessionbecamebegin_user_session(desktop and per-user) orbegin_app_session(server/app). The SDKs choose the right one; in .NET callControlManager.ValidateSessionAsync(). - License key moved to registration: pass it to
RegisterAsync(licenseKey, userInfo), not to theControlManagerconstructor. - Re-registration: existing instances keep their registration; persist and reload
ValidationStateso the new build does not register again.
SDK Upgrade Notes
Current releases of every SDK except Python target client API v3.4. Python targets v3.2.
| SDK | Package | Released version | API | Custom data (v3.5) |
|---|---|---|---|---|
| .NET (reference) | NuGet evoleap.Licensing |
5.4.1 | v3.4 | Preview ValidationState.CustomData,
SessionState.CustomData |
| Java | Maven com.evoleap.licensing:com.evoleap.licensing |
1.1.0 | v3.4 | Preview parsed by the web-service layer only |
| JavaScript / TypeScript | npm evoleap-licensing |
0.7.108 | v3.4 | Preview customData on the user state |
| Python | PyPI evoleap_licensing |
1.1.4 | v3.2 | Preview parsed on the branch, but the SDK calls v3.2 endpoints, so none is returned yet |
| C++ | vcpkg port evoleap-licensing |
0.7.0 | v3.4 | Preview parsed on session results only |
| Ruby | RubyGems evoleap-licensing |
1.0.3.18 | v3.4 | Preview session_state.custom_data |
Changes in current releases
- Unlimited entitlements: the .NET, Java, JavaScript, Python and Ruby SDKs now handle licenses and component entitlements with unlimited counts correctly. Update if you issue unlimited licenses.
- Expired sessions (.NET, Java): set
ControlStrategy.StartSessionForExpiredSession = trueto end an expired session and start a new one automatically on the next validation (defaultfalse). - UTF-8: .NET, Java and C++ send UTF-8, so user names and license data with non-ASCII characters round-trip correctly.
- .NET: set
ConnectionSettings.Log(Action<Exception, string>) to log HTTPS and RSA errors. The package assemblies, including the licensing model and hardware-info DLLs, are now signed. The obsoleteUserControlManager.CheckoutFullLicenseAsyncandUserControlManager.RegisterOfflineAsyncwere removed (Feb 2025); useCheckOutFullLicenseAsync/RegisterOfflineAsynconServerControlManagerorControlManager. - Python: the released SDK covers desktop licensing (
ControlManager) and calls v3.2 endpoints. - Ruby: SDK for Ruby/Rails web apps (Ruby 3.1+): server instance registration, per-user sessions, components and grace periods.
- Server-side entitlement check: a server acting for a desktop client can ask
what that client's session is entitled to with the access-token endpoint
POST /api/session_entitlements(session_validatescope). It is read-only and does not extend the session.
Preview — API v3.5, not yet released. The following is on development branches of the backend and SDKs and may change before release.
- License properties / custom data: vendors set key/value properties on a
license (in the portal or with the
set_license_propertiesaction). v3.5 returns them ascustom_dataon successful session responses, and the SDKs cache them in state like features (see the table above for where each SDK exposes them). validate_user: lets a web app check whether a user is still entitled without starting a session or using a seat (RubyServerControlManager#validate_user).- Python parity: server-side licensing (
ServerControlManager,UserControlManager), offline checkout and the remaining web-service calls. The branch still sends requests to the v3.2 endpoint, so its v3.3/v3.4 calls will need a newer endpoint before release.
// .NET (preview): read a license property after validating
var validity = await controlManager.ValidateSessionAsync();
if (validity.IsValid &&
controlManager.ValidationState.CustomData.TryGetValue("max_projects", out var value)) {
maxProjects = int.Parse(value); // all values are strings
}
// JavaScript (preview): the default prefix is v3.4; opt in to v3.5
import { user } from 'evoleap-licensing';
const ucm = user.getUserControlManager(
instanceId, serverManager, userState, sessionState,
'https://elm.evoleap.com/api/v3.5/auth');
// after a successful validateSession, the user state carries customData
Data Migration Strategies
Successfully migrating customer licenses and historical data requires careful planning and execution.
Customer License Migration
1. Data Export and Analysis
Extract customer license data from your existing system:
// Example: Export existing license data
public class LicenseExporter {
public List<LegacyLicense> ExportLicenses() {
var licenses = new List<LegacyLicense>();
// Extract from your existing system
foreach (var customer in GetAllCustomers()) {
var license = new LegacyLicense {
LicenseId = customer.LicenseId,
CustomerId = customer.Id,
CustomerName = customer.Name,
Email = customer.Email,
ProductName = customer.ProductName,
LicenseType = customer.LicenseType, // Named, Concurrent, etc.
UserLimit = customer.UserLimit,
ExpirationDate = customer.ExpirationDate,
EnabledFeatures = customer.Features.ToList(),
HardwareId = customer.HardwareId,
ActivationDate = customer.ActivationDate,
IsActive = customer.IsActive
};
licenses.Add(license);
}
return licenses;
}
}
2. License Mapping and Conversion
Map each legacy license onto the request body of the elm
create_license action. elm has four license types and five commercial models,
addressed by numeric id:
| Field | Values | Notes |
|---|---|---|
license.license_type_id |
1 Named User, 2 Concurrent, 3 Unrestricted, 4 Node Locked | Decides which seat count applies (users, sessions or instances) |
license.commercial_model_id |
1 Perpetual, 2 SaaS, 3 Lease, 4 Free Trial, 5 Free | Free licenses are always Unrestricted |
entitlement.seat_count |
integer | Applied as user, session or instance count according to the license type |
term.start_date / term.end_date |
ISO dates | License term; required for every model except Free |
license.features / license.components |
arrays of names | Must already exist on the product in the elm portal |
// Example: map a legacy license to an elm create_license request body
public class LicenseMapper {
private const string ElmProductName = "My Product"; // product NAME as shown in the portal
public object MapToElm(LegacyLicense legacy) {
return new {
license = new {
product = ElmProductName,
company_name = legacy.CustomerName, // or company_id / crm_id to attach
company_email = legacy.Email, // to an existing company
crm_id = legacy.CustomerId,
license_type_id = MapLicenseType(legacy.LicenseType),
commercial_model_id = legacy.ExpirationDate.HasValue ? 2 /* SaaS */ : 1 /* Perpetual */,
features = MapFeatures(legacy.EnabledFeatures),
description = $"Migrated from legacy license {legacy.LicenseId}"
},
entitlement = new { seat_count = legacy.UserLimit },
term = new {
start_date = legacy.ActivationDate.ToString("yyyy-MM-dd"),
end_date = legacy.ExpirationDate?.ToString("yyyy-MM-dd")
},
comment = "Legacy license migration"
};
}
private int MapLicenseType(string legacyType) {
return legacyType.ToLower() switch {
"named" => 1, // Named User
"concurrent" => 2, // Concurrent
"unlimited" => 3, // Unrestricted
"site" => 4, // Node Locked (per machine)
_ => 1
};
}
private List<string> MapFeatures(List<string> legacyFeatures) {
// Map legacy feature names to the feature names defined in the elm portal
return legacyFeatures.Select(f => f switch {
"advanced_reporting" => "AdvancedReporting",
"api_access" => "APIAccess",
_ => f
}).ToList();
}
}
3. Bulk License Creation
Create licenses through the elm license-administration API. Requests are authenticated with a
personal access token (PAT) created in the elm portal, sent as
Authorization: Bearer <token>. A token with the license_admin
scope applies changes immediately; with only license_maintainer, actions are
queued for approval in the portal. elm does not ship a client library for this API; the
example below calls it with HttpClient.
Things to know before a bulk import:
- Keys are generated by elm.
create_licensecannot import an existing key; the response contains the newlicense_key, which you send to the customer. - Keep the legacy id. Put it in the license
description(as below) and keep the legacy-id-to-key mapping in your own system. Preview With API v3.5,set_license_propertieswill store it as a license property returned to clients ascustom_data(see SDK Upgrade Notes). - Avoid duplicate companies. Look up existing ones with
GET /api/companies?q=and passcompany_id.
// Example: bulk license migration through the elm PAT API
public class BulkMigration {
private readonly HttpClient http = new HttpClient {
BaseAddress = new Uri("https://elm.evoleap.com/")
};
public BulkMigration(string personalAccessToken) {
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", personalAccessToken);
}
public async Task<MigrationResult> MigrateLicensesAsync(List<LegacyLicense> legacyLicenses) {
var results = new MigrationResult();
var mapper = new LicenseMapper();
foreach (var legacy in legacyLicenses) {
var response = await http.PostAsJsonAsync(
"api/license_actions/create_license", mapper.MapToElm(legacy));
var body = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode) {
results.FailedMigrations.Add(new MigrationFailure {
CustomerId = legacy.CustomerId,
Error = body.ToString(),
LegacyLicense = legacy
});
continue;
}
var licenseKey = body.GetProperty("license_key").GetString();
// PREVIEW (API v3.5, not yet released): also store the legacy id as a
// license property so clients receive it as custom_data. Replaces all properties.
// await http.PostAsJsonAsync("api/license_actions/set_license_properties", new {
// license_key = licenseKey,
// properties = new[] { new { key = "legacy_license_id", value = legacy.LicenseId } }
// });
results.SuccessfulMigrations.Add(new MigrationMapping {
CustomerId = legacy.CustomerId,
CustomerEmail = legacy.Email,
OldLicenseId = legacy.LicenseId,
NewLicenseKey = licenseKey
});
await UpdateCustomerDatabase(legacy.CustomerId, licenseKey);
}
return results;
}
}
Other actions on the same API include modify_license,
modify_entitlement, create_term, add_feature,
create_component_entitlement, deactivate_license and
activate_license (all POST /api/license_actions/<action>,
addressed by license_key). Read-only endpoints include GET /api/licenses,
GET /api/licenses/show?license_key= and GET /api/users.
Historical Data Migration
Usage History and Analytics
Consider what historical data to preserve:
- License Activation History: When customers first activated licenses
- Usage Patterns: Historical usage data for analytics
- Support History: Previous license-related support cases
- Billing Records: Payment and renewal history
- User Assignments: Historical user-to-license mappings
Phased Migration Approach
For large deployments, a gradual migration minimizes risk and allows for learning and adjustment.
Four-Phase Migration Strategy
Phase 1: Dual Stack Deployment
Goal: Deploy elm alongside existing licensing for testing and validation
- Install elm SDK in parallel with existing licensing
- Implement feature flags to switch between systems
- Test with internal users and beta customers
- Validate license migration scripts and processes
- Train support team on elm platform
// Dual stack implementation example
public class HybridLicenseManager {
private readonly LegacyLicenseManager legacyManager;
private readonly ControlManager elmManager;
private readonly bool useElm; // Feature flag
public async Task<bool> ValidateLicenseAsync() {
if (useElm) {
var sessionValidity = await elmManager.ValidateSessionAsync();
return sessionValidity.IsValid;
} else {
return legacyManager.ValidateLicense();
}
}
}
Phase 2: New Customer Migration
Goal: All new customers use elm exclusively
- Configure sales and provisioning systems for elm
- Update onboarding processes and documentation
- Migrate pilot existing customers to validate processes
- Collect feedback and refine migration procedures
- Monitor elm system performance and reliability
Phase 3: Existing Customer Migration
Goal: Migrate existing customers during renewal cycles
- Migrate customers at renewal time
- Provide migration assistance and support
- Handle special cases and custom requirements
- Maintain legacy system for remaining customers
- Communicate migration benefits and timeline
Phase 4: Legacy System Decommission
Goal: Complete migration and shutdown legacy systems
- Migrate remaining customers with incentives if needed
- Archive legacy license data and documentation
- Decommission legacy license servers and infrastructure
- Remove legacy code from applications
- Celebrate successful migration completion!
Risk Mitigation Strategies
Rollback Planning
Always have a rollback plan in case of issues:
- Feature Flags: Ability to instantly revert to legacy system
- Data Backup: Complete backup of legacy license data
- Parallel Operation: Keep legacy system running during transition
- Customer Communication: Clear communication about any issues
- Support Escalation: Direct escalation path to elm support team
Success Metrics
Define clear metrics to measure migration success:
- Technical Metrics: System uptime, API response times, error rates
- Customer Metrics: Customer satisfaction, support ticket volume
- Business Metrics: License utilization, revenue impact, operational costs
- Operational Metrics: Migration velocity, rollback incidents, team productivity
Common Migration Challenges and Solutions
Learn from common migration challenges and their proven solutions.
Challenge: Complex Feature Mappings
Problem: Legacy licensing has complex feature hierarchies or dependencies that don't map directly to elm's feature model.
Solution: Model editions and bundles as feature groups in the elm portal (they
are reported in ValidationState.FeatureGroups), and keep any remaining dependency
logic in a small adapter:
// Handling complex feature relationships in elm
public class FeatureManager {
private readonly ControlManager controlManager;
public bool IsFeatureAvailable(string featureName) {
// Simple feature check
if (controlManager.ValidationState.Features.Contains(featureName)) {
return true;
}
// Handle feature dependencies and hierarchies
return featureName switch {
"AdvancedReporting" => HasReportingBundle(),
"DataExport" => HasDataAccess() && HasExportRights(),
"APIAccess" => HasIntegrationPackage(),
_ => false
};
}
private bool HasReportingBundle() {
return controlManager.ValidationState.Features.Contains("ReportingBundle") ||
controlManager.ValidationState.Features.Contains("EnterpriseEdition");
}
private bool HasDataAccess() {
return controlManager.ValidationState.Features.Contains("DataAccess") ||
controlManager.ValidationState.Features.Contains("ProfessionalEdition") ||
controlManager.ValidationState.Features.Contains("EnterpriseEdition");
}
}
Challenge: Customer Resistance to Change
Problem: Existing customers are reluctant to migrate to a new licensing system.
Solution: Provide clear value proposition and migration support:
- Improved Reliability: Hosted licensing instead of self-managed license servers
- Better User Experience: Simpler activation and license management
- Enhanced Security: Modern encryption and cloud-based validation
- New Capabilities: Offline checkout, component tokens, usage analytics
- Reduced IT Overhead: No license servers to maintain
- Migration Assistance: Professional support during transition
- Incentives: Consider offering migration incentives or grace periods
Challenge: State Migration and Compatibility
Problem: Existing installations hold legacy activation state that doesn't map to elm's state model.
Solution: Don't convert legacy state into elm state. elm state
(IValidationState) records a server-side registration, so it must come from
RegisterAsync. On the first launch of the elm-enabled version, use the legacy
activation only to find the customer's new elm license key, then register:
// First launch after upgrading: bridge legacy activation to elm registration
var savedState = LoadElmState(); // null on first launch
var controlManager = new ControlManager(productId, version, publicKey,
userIdentity, instanceIdentity, controlStrategy, savedState);
if (!controlManager.ValidationState.Registered) {
var legacy = LegacyLicenseStore.TryRead();
// Your own lookup (e.g. your customer portal) from legacy license id to the
// elm key created during data migration; fall back to asking the user.
var elmKey = legacy != null
? await LookUpMigratedKeyAsync(legacy.LicenseId)
: PromptForLicenseKey();
var result = await controlManager.RegisterAsync(elmKey,
new UserInfo(legacy?.UserName ?? "", legacy?.Email ?? ""));
if (result.Success) {
SaveElmState(controlManager.ValidationState);
LegacyLicenseStore.MarkMigrated(); // keep it until registration succeeds
}
}
var validity = await controlManager.ValidateSessionAsync();
SaveElmState(controlManager.ValidationState);
Challenge: Network and Connectivity Requirements
Problem: Some customer environments have restricted internet access.
Solution: Use elm's connectivity options:
- Proxy Configuration: The .NET SDK uses the system default proxy with the
current user's credentials; elm needs outbound HTTPS to
elm.evoleap.com - Grace Periods: The license's grace period (set in the portal) keeps sessions valid while elm is unreachable
- Offline Checkout: Check out the license, or component tokens, for a fixed period before going offline
- elm Lite: A local license server for isolated networks, fed by a full-license checkout (API v3.4+)
// Connection settings are static and apply to every ControlManager
ConnectionSettings.RequestTimeout = TimeSpan.FromSeconds(30);
// Only for elm Lite or a dedicated elm host - not for a proxy:
// ConnectionSettings.Host = "https://elm-lite.internal.example.com";
// Allow use before activation completes (e.g. while waiting on IT)
var strategy = new ControlStrategy {
GracePeriodForUnregisteredProduct = TimeSpan.FromDays(7)
};
// Before going offline: check out the license for up to 30 days
var offline = await controlManager.CheckOutLicenseForOfflineUseAsync(TimeSpan.FromDays(30));
if (!offline.Success) {
ShowMessage($"Offline checkout failed: {offline.FailureReason}");
} else {
ShowMessage($"Licensed offline until {offline.ExpirationTime:g}");
}
// Back online: return it early so the seat is released
await controlManager.CheckInOfflineLicenseAsync(
controlManager.ValidationState.OfflineCheckouts.First().CheckoutKey);
Challenge: Integration with Existing Business Systems
Problem: CRM, billing, and support systems need to integrate with elm instead of legacy licensing.
Solution: Drive elm from those systems with the PAT license-administration API (see Data Migration for authentication):
// Integration with CRM and billing systems (http is an HttpClient
// with BaseAddress https://elm.evoleap.com/ and a Bearer PAT)
public class ElmBusinessIntegration {
private readonly HttpClient http;
private readonly CrmSystem crmSystem;
// Pull the current state of a license into the CRM (license_read scope)
public async Task SyncLicenseAsync(string customerId, string licenseKey) {
var license = await http.GetFromJsonAsync<JsonElement>(
$"api/licenses/show?license_key={Uri.EscapeDataString(licenseKey)}");
await crmSystem.UpdateLicenseStatusAsync(customerId, license);
}
// Renewal: add a new term to the existing license; the key stays the same
public async Task<bool> ProcessRenewalAsync(string licenseKey, DateTime start, DateTime end) {
var response = await http.PostAsJsonAsync("api/license_actions/create_term", new {
license_key = licenseKey,
term = new { start_date = start.ToString("yyyy-MM-dd"), end_date = end.ToString("yyyy-MM-dd") },
comment = "Renewal from billing system"
});
return response.IsSuccessStatusCode;
}
// Seat change after an upgrade/downgrade
public Task<HttpResponseMessage> SetSeatsAsync(string licenseKey, int seats) =>
http.PostAsJsonAsync("api/license_actions/modify_entitlement", new {
license_key = licenseKey,
entitlement = new { seat_count = seats }
});
}
Migration Timeline Template
Use this timeline template as a starting point for planning your elm migration project.
Weeks 1-2: Assessment and Planning
Goals: Understand current state and design migration approach
Discovery Tasks:
- Audit existing licensing implementation and integration points
- Document current license models, features, and restrictions
- Inventory customer licenses and identify special cases
- Map current features to elm feature/component model
- Identify integration requirements (CRM, billing, support systems)
Planning Tasks:
- Create elm product configuration design
- Develop migration project plan and timeline
- Set up elm development environment and test accounts
- Design license migration scripts and data mapping
- Plan customer communication strategy
Deliverables:
- Current state assessment document
- elm product configuration specification
- Migration project plan with timeline and resources
- Risk assessment and mitigation strategies
Weeks 3-6: Development and Testing
Goals: Implement elm integration and validate migration processes
Development Tasks:
- Integrate elm SDK into application(s)
- Implement state management and persistence
- Add feature/component access control
- Implement comprehensive error handling
- Create license migration scripts and tools
- Develop dual-stack support for gradual migration
Testing Tasks:
- Unit testing of elm integration
- Integration testing with various license scenarios
- Performance testing and optimization
- Test migration scripts with sample data
- User acceptance testing with internal teams
Deliverables:
- Application with elm SDK integration
- Comprehensive test suite
- License migration tools and scripts
- Performance benchmarks and optimization report
Weeks 7-8: Pilot Deployment
Goals: Validate migration process with real customers
Pilot Tasks:
- Deploy to staging environment
- Select and migrate 5-10 pilot customers
- Monitor system performance and reliability
- Collect feedback from pilot customers
- Test customer support processes
- Refine migration procedures based on learnings
Validation Tasks:
- Verify all license scenarios work correctly
- Test offline and connectivity edge cases
- Validate business system integrations
- Confirm customer communication processes
Deliverables:
- Pilot migration results and lessons learned
- Refined migration procedures
- Customer feedback analysis
- Go/no-go decision for full migration
Weeks 9-12: Full Migration
Goals: Complete migration of all customers
Production Tasks:
- Production deployment of elm integration
- Begin new customer onboarding with elm
- Execute bulk migration of existing customers
- Provide customer migration assistance and support
- Monitor system performance and customer satisfaction
Communication Tasks:
- Send migration announcements to customers
- Provide migration guides and documentation
- Host migration webinars and support sessions
- Handle customer questions and concerns
- Update marketing and sales materials
Deliverables:
- Production elm deployment
- All customers migrated to elm
- Legacy system decommissioning plan
- Post-migration performance report
Weeks 13-16: Optimization and Cleanup
Goals: Optimize performance and complete migration
Optimization Tasks:
- Performance tuning based on production usage
- Address any customer issues or edge cases
- Optimize license utilization and allocation
- Fine-tune monitoring and alerting
Cleanup Tasks:
- Remove legacy licensing code from applications
- Decommission legacy license servers
- Archive legacy license data
- Update documentation and procedures
- Conduct post-migration review and lessons learned
Deliverables:
- Optimized elm deployment
- Legacy system fully decommissioned
- Updated documentation and procedures
- Migration project completion report
Timeline Considerations
Factors That May Extend Timeline
- Complex Legacy System: Heavily customized licensing may require additional mapping
- Large Customer Base: More customers mean more migration coordination
- Multiple Products: Each product may need separate migration planning
- Extensive Integrations: CRM, billing, and support system integrations take time
- Custom Requirements: Special customer needs may require custom development
- Regulatory Compliance: Some industries have specific licensing audit requirements
Factors That May Accelerate Timeline
- Simple Legacy System: Basic licensing models migrate quickly
- Cloud-Native Applications: Already cloud-ready applications integrate faster
- Professional Services: elm migration consultants can accelerate the process
- Staged Rollout: Phased approach allows parallel development and migration
- Modern SDK Usage: Applications already using modern patterns integrate more easily
Professional Migration Support
evoleap offers migration support to help your transition to elm go smoothly.
Support Options
- Consulting Services: Guidance on migration planning, product configuration and license mapping
- Technical Support: Help with SDK integration and migration-related issues
- Staging Accounts: A free account on the elm staging server
(
elm-staging.evoleap.com) for testing your integration and migration scripts - Documentation: This site, plus the README and samples shipped with each SDK
Getting Started with Migration Support
- Contact evoleap to discuss your migration needs
- Schedule a discovery call to assess your current licensing setup
- Get a staging account and develop a migration plan with evoleap
- Prepare your team with the integration guides on this site
Contact Information
- Email: info@evoleap.com
- Phone: +1 (832) 371-6677
See also evoleap support and contact options.