Skip to content

Commit 7897e24

Browse files
authored
Merge pull request #18 from Keyfactor/migrate_instance
Migrate Instance
2 parents a770d9f + 2c2970a commit 7897e24

File tree

12 files changed

+865
-20
lines changed

12 files changed

+865
-20
lines changed

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,40 @@ kfutil stores inventory remove \
364364
--cn <additional cert subject name>
365365
```
366366
367+
### Export Instance Data
368+
369+
For full documentation, see [export](docs/kfutil_export.md).
370+
371+
Export select instance data to JSON file:
372+
373+
```bash
374+
# export only collections, metadata, and roles
375+
kfutil export --collections --metadata --roles --file <path to JSON file>
376+
```
377+
378+
Export all exportable instance data to JSON file:
379+
380+
```bash
381+
kfutil export --all --file <path to JSON file>
382+
```
383+
384+
### Import Instance Data
385+
386+
For full documentation, see [import](docs/kfutil_import.md).
387+
388+
Import select instance data from exported JSON file:
389+
390+
```bash
391+
# export only collections, metadata, and roles
392+
kfutil import --collections --metadata --roles --file <path to JSON file>
393+
```
394+
395+
Import all importable instance data from exported JSON file:
396+
397+
```bash
398+
kfutil import --all --file <path to JSON file>
399+
```
400+
367401
## Development
368402
369403
This CLI developed using [cobra](https://umarcor.github.io/cobra/)
@@ -379,4 +413,3 @@ alternatively you can specify the parent command
379413
```bash
380414
cobra-cli add <my-new-command> -p '<parent>Cmd'
381415
```
382-

cmd/export.go

Lines changed: 415 additions & 0 deletions
Large diffs are not rendered by default.

cmd/import.go

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
keyfactor_command_client_api "github.com/Keyfactor/keyfactor-go-client-sdk"
8+
"github.com/Keyfactor/keyfactor-go-client/api"
9+
"github.com/spf13/cobra"
10+
"io"
11+
"log"
12+
"os"
13+
)
14+
15+
type Body struct {
16+
ErrorCode string
17+
Message string
18+
}
19+
20+
func parseError(error io.ReadCloser) string {
21+
bytes, _ := io.ReadAll(error)
22+
var newError Body
23+
json.Unmarshal(bytes, &newError)
24+
return newError.Message
25+
}
26+
27+
var importCmd = &cobra.Command{
28+
Use: "import",
29+
Short: "Keyfactor instance import utilities.",
30+
Long: `A collection of APIs and utilities for importing Keyfactor instance data.`,
31+
Run: func(cmd *cobra.Command, args []string) {
32+
exportPath := cmd.Flag("file").Value.String()
33+
jsonFile, oErr := os.Open(exportPath)
34+
if oErr != nil {
35+
fmt.Printf("Error opening exported file: %s\n", oErr)
36+
log.Fatalf("Error: %s", oErr)
37+
}
38+
defer jsonFile.Close()
39+
var out outJson
40+
bJson, _ := io.ReadAll(jsonFile)
41+
jErr := json.Unmarshal(bJson, &out)
42+
if jErr != nil {
43+
fmt.Printf("Error reading exported file: %s\n", jErr)
44+
log.Fatalf("Error: %s", jErr)
45+
}
46+
kfClient := initGenClient()
47+
oldKfClient, _ := initClient()
48+
if cmd.Flag("all").Value.String() == "true" {
49+
importCollections(out.Collections, kfClient)
50+
importMetadataFields(out.MetadataFields, kfClient)
51+
importIssuedCertAlerts(out.IssuedCertAlerts, kfClient)
52+
importDeniedCertAlerts(out.DeniedCertAlerts, kfClient)
53+
importPendingCertAlerts(out.PendingCertAlerts, kfClient)
54+
importNetworks(out.Networks, kfClient)
55+
importWorkflowDefinitions(out.WorkflowDefinitions, kfClient)
56+
importBuiltInReports(out.BuiltInReports, kfClient)
57+
importCustomReports(out.CustomReports, kfClient)
58+
importSecurityRoles(out.SecurityRoles, oldKfClient)
59+
} else {
60+
if len(out.Collections) != 0 && cmd.Flag("collections").Value.String() == "true" {
61+
importCollections(out.Collections, kfClient)
62+
}
63+
if len(out.MetadataFields) != 0 && cmd.Flag("metadata").Value.String() == "true" {
64+
importMetadataFields(out.MetadataFields, kfClient)
65+
}
66+
if len(out.IssuedCertAlerts) != 0 && cmd.Flag("issued-alerts").Value.String() == "true" {
67+
importIssuedCertAlerts(out.IssuedCertAlerts, kfClient)
68+
}
69+
if len(out.DeniedCertAlerts) != 0 && cmd.Flag("denied-alerts").Value.String() == "true" {
70+
importDeniedCertAlerts(out.DeniedCertAlerts, kfClient)
71+
}
72+
if len(out.PendingCertAlerts) != 0 && cmd.Flag("pending-alerts").Value.String() == "true" {
73+
importPendingCertAlerts(out.PendingCertAlerts, kfClient)
74+
}
75+
if len(out.Networks) != 0 && cmd.Flag("networks").Value.String() == "true" {
76+
importNetworks(out.Networks, kfClient)
77+
}
78+
if len(out.WorkflowDefinitions) != 0 && cmd.Flag("workflow-definitions").Value.String() == "true" {
79+
importWorkflowDefinitions(out.WorkflowDefinitions, kfClient)
80+
}
81+
if len(out.BuiltInReports) != 0 && cmd.Flag("reports").Value.String() == "true" {
82+
importBuiltInReports(out.BuiltInReports, kfClient)
83+
}
84+
if len(out.CustomReports) != 0 && cmd.Flag("reports").Value.String() == "true" {
85+
importCustomReports(out.CustomReports, kfClient)
86+
}
87+
if len(out.SecurityRoles) != 0 && cmd.Flag("security-roles").Value.String() == "true" {
88+
importSecurityRoles(out.SecurityRoles, oldKfClient)
89+
}
90+
}
91+
},
92+
}
93+
94+
func importCollections(collections []keyfactor_command_client_api.KeyfactorApiModelsCertificateCollectionsCertificateCollectionCreateRequest, kfClient *keyfactor_command_client_api.APIClient) {
95+
for _, collection := range collections {
96+
_, httpResp, reqErr := kfClient.CertificateCollectionApi.CertificateCollectionCreateCollection(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).
97+
Request(collection).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
98+
name, _ := json.Marshal(collection.Name)
99+
if reqErr != nil {
100+
fmt.Printf("%s Error! Unable to create collection %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
101+
} else {
102+
name, _ := json.Marshal(collection.Name)
103+
fmt.Println("Added", string(name), "to collections")
104+
}
105+
}
106+
}
107+
108+
func importMetadataFields(metadataFields []keyfactor_command_client_api.KeyfactorApiModelsMetadataFieldMetadataFieldCreateRequest, kfClient *keyfactor_command_client_api.APIClient) {
109+
for _, metadata := range metadataFields {
110+
_, httpResp, reqErr := kfClient.MetadataFieldApi.MetadataFieldCreateMetadataField(context.Background()).
111+
XKeyfactorRequestedWith(xKeyfactorRequestedWith).MetadataFieldType(metadata).
112+
XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
113+
name, _ := json.Marshal(metadata.Name)
114+
if reqErr != nil {
115+
fmt.Printf("%s Error! Unable to create metadata field type %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
116+
} else {
117+
fmt.Println("Added", string(name), "to metadata field types.")
118+
}
119+
}
120+
}
121+
122+
func importIssuedCertAlerts(alerts []keyfactor_command_client_api.KeyfactorApiModelsAlertsIssuedIssuedAlertCreationRequest, kfClient *keyfactor_command_client_api.APIClient) {
123+
for _, alert := range alerts {
124+
_, httpResp, reqErr := kfClient.IssuedAlertApi.IssuedAlertAddIssuedAlert(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
125+
name, _ := json.Marshal(alert.DisplayName)
126+
if reqErr != nil {
127+
fmt.Printf("%s Error! Unable to create issued cert alert %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
128+
} else {
129+
fmt.Println("Added", string(name), "to issued cert alerts.")
130+
}
131+
}
132+
}
133+
134+
func importDeniedCertAlerts(alerts []keyfactor_command_client_api.KeyfactorApiModelsAlertsDeniedDeniedAlertCreationRequest, kfClient *keyfactor_command_client_api.APIClient) {
135+
for _, alert := range alerts {
136+
_, httpResp, reqErr := kfClient.DeniedAlertApi.DeniedAlertAddDeniedAlert(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
137+
name, _ := json.Marshal(alert.DisplayName)
138+
if reqErr != nil {
139+
fmt.Printf("%s Error! Unable to create denied cert alert %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
140+
} else {
141+
fmt.Println("Added", string(name), "to denied cert alerts.")
142+
}
143+
}
144+
}
145+
146+
func importPendingCertAlerts(alerts []keyfactor_command_client_api.KeyfactorApiModelsAlertsPendingPendingAlertCreationRequest, kfClient *keyfactor_command_client_api.APIClient) {
147+
for _, alert := range alerts {
148+
_, httpResp, reqErr := kfClient.PendingAlertApi.PendingAlertAddPendingAlert(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
149+
name, _ := json.Marshal(alert.DisplayName)
150+
if reqErr != nil {
151+
fmt.Printf("%s Error! Unable to create pending cert alert %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
152+
} else {
153+
fmt.Println("Added", string(name), "to pending cert alerts.")
154+
}
155+
}
156+
}
157+
158+
func importNetworks(networks []keyfactor_command_client_api.KeyfactorApiModelsSslCreateNetworkRequest, kfClient *keyfactor_command_client_api.APIClient) {
159+
for _, network := range networks {
160+
_, httpResp, reqErr := kfClient.SslApi.SslCreateNetwork(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Network(network).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
161+
name, _ := json.Marshal(network.Name)
162+
if reqErr != nil {
163+
fmt.Printf("%s Error! Unable to create SSL network %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
164+
} else {
165+
fmt.Println("Added", string(name), "to SSL networks.")
166+
}
167+
}
168+
}
169+
170+
// identify matching templates between instances by name, then return the template Id of the matching template in the import instance
171+
func findMatchingTemplates(exportedWorkflowDef exportKeyfactorApiModelsWorkflowsDefinitionCreateRequest, kfClient *keyfactor_command_client_api.APIClient) *string {
172+
importInstanceTemplates, _, _ := kfClient.TemplateApi.TemplateGetTemplates(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
173+
for _, template := range importInstanceTemplates {
174+
importInstTempNameJson, _ := json.Marshal(template.TemplateName)
175+
exportInstTempNameJson, _ := json.Marshal(exportedWorkflowDef.KeyName)
176+
if string(importInstTempNameJson) == string(exportInstTempNameJson) {
177+
importInstMatchingTemplateId, _ := json.Marshal(template.Id)
178+
importInstMatchingTemplateIdStr := string(importInstMatchingTemplateId)
179+
return &importInstMatchingTemplateIdStr
180+
}
181+
}
182+
return nil
183+
}
184+
185+
func importWorkflowDefinitions(workflowDefs []exportKeyfactorApiModelsWorkflowsDefinitionCreateRequest, kfClient *keyfactor_command_client_api.APIClient) {
186+
for _, workflowDef := range workflowDefs {
187+
wJson, _ := json.Marshal(workflowDef)
188+
var workflowDefReq keyfactor_command_client_api.KeyfactorApiModelsWorkflowsDefinitionCreateRequest
189+
jErr := json.Unmarshal(wJson, &workflowDefReq)
190+
if jErr != nil {
191+
fmt.Printf("Error: %s\n", jErr)
192+
log.Fatalf("Error: %s", jErr)
193+
}
194+
newTemplateId := findMatchingTemplates(workflowDef, kfClient)
195+
if newTemplateId != nil {
196+
workflowDefReq.Key = newTemplateId
197+
}
198+
_, httpResp, reqErr := kfClient.WorkflowDefinitionApi.WorkflowDefinitionCreateNewDefinition(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Request(workflowDefReq).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
199+
name, _ := json.Marshal(workflowDef.DisplayName)
200+
if reqErr != nil {
201+
fmt.Printf("%s Error! Unable to create workflow definition %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
202+
} else {
203+
fmt.Println("Added", string(name), "to workflow definitions.")
204+
}
205+
}
206+
}
207+
208+
// check for built-in report discrepancies between instances, return the report id of reports that need to be updated in import instance
209+
func checkBuiltInReportDiffs(exportedReport exportModelsReport, kfClient *keyfactor_command_client_api.APIClient) *int32 {
210+
importInstanceReports, _, _ := kfClient.ReportsApi.ReportsQueryReports(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
211+
//check if built in report was modified from default in exported instance; if modified, update built-in report in new instance
212+
for _, report := range importInstanceReports {
213+
importInstDispNameJson, _ := json.Marshal(report.DisplayName)
214+
exportInstDispNameJson, _ := json.Marshal(exportedReport.DisplayName)
215+
importInstInNavJson, _ := json.Marshal(report.InNavigator)
216+
exportInstInNavJson, _ := json.Marshal(exportedReport.InNavigator)
217+
importInstFavJson, _ := json.Marshal(report.Favorite)
218+
exportInstFavJson, _ := json.Marshal(exportedReport.Favorite)
219+
importInstRemDupJson, _ := json.Marshal(report.RemoveDuplicates)
220+
exportInstRemDupJson, _ := json.Marshal(exportedReport.RemoveDuplicates)
221+
var usesCollectionBool = "false"
222+
if string(importInstDispNameJson) == string(exportInstDispNameJson) {
223+
if (string(importInstFavJson) != string(exportInstFavJson)) || (string(importInstInNavJson) != string(exportInstInNavJson)) || (string(importInstRemDupJson) != string(exportInstRemDupJson)) {
224+
usesCollectionJson, _ := json.Marshal(exportedReport.UsesCollection)
225+
if string(usesCollectionJson) == usesCollectionBool {
226+
return report.Id
227+
}
228+
}
229+
}
230+
}
231+
return nil
232+
}
233+
234+
// only imports built in reports where UsesCollections is false
235+
func importBuiltInReports(reports []exportModelsReport, kfClient *keyfactor_command_client_api.APIClient) {
236+
for _, report := range reports {
237+
newReportId := checkBuiltInReportDiffs(report, kfClient)
238+
if newReportId != nil {
239+
rJson, _ := json.Marshal(report)
240+
var reportReq keyfactor_command_client_api.ModelsReportRequestModel
241+
jErr := json.Unmarshal(rJson, &reportReq)
242+
if jErr != nil {
243+
fmt.Printf("Error: %s\n", jErr)
244+
log.Fatalf("Error: %s", jErr)
245+
}
246+
reportReq.Id = newReportId
247+
_, httpResp, reqErr := kfClient.ReportsApi.ReportsUpdateReport(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Request(reportReq).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
248+
name, _ := json.Marshal(report.DisplayName)
249+
if reqErr != nil {
250+
fmt.Printf("%s Error! Unable to update built-in report %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
251+
} else {
252+
fmt.Println("Updated", string(name), "in built-in reports.")
253+
}
254+
}
255+
}
256+
}
257+
258+
func importCustomReports(reports []keyfactor_command_client_api.ModelsCustomReportCreationRequest, kfClient *keyfactor_command_client_api.APIClient) {
259+
for _, report := range reports {
260+
_, httpResp, reqErr := kfClient.ReportsApi.ReportsCreateCustomReport(context.Background()).XKeyfactorRequestedWith(xKeyfactorRequestedWith).Request(report).XKeyfactorApiVersion(xKeyfactorApiVersion).Execute()
261+
name, _ := json.Marshal(report.DisplayName)
262+
if reqErr != nil {
263+
fmt.Printf("%s Error! Unable to create custom report %s - %s%s\n", colorRed, string(name), parseError(httpResp.Body), colorWhite)
264+
} else {
265+
fmt.Println("Added", string(name), "to custom reports.")
266+
}
267+
}
268+
}
269+
270+
func importSecurityRoles(roles []api.CreateSecurityRoleArg, kfClient *api.Client) {
271+
for _, role := range roles {
272+
_, reqErr := kfClient.CreateSecurityRole(&role)
273+
name, _ := json.Marshal(role.Name)
274+
if reqErr != nil {
275+
fmt.Printf("%s Error! Unable to create security role %s - %s%s\n", colorRed, string(name), reqErr, colorWhite)
276+
} else {
277+
fmt.Println("Added", string(name), "to security roles.")
278+
}
279+
}
280+
}
281+
282+
func init() {
283+
RootCmd.AddCommand(importCmd)
284+
285+
importCmd.Flags().StringVarP(&exportPath, "file", "f", "", "import JSON to a specified filepath")
286+
importCmd.MarkFlagRequired("file")
287+
288+
importCmd.Flags().BoolVarP(&fAll, "all", "a", false, "import all importable data to JSON file")
289+
importCmd.Flags().Lookup("all").NoOptDefVal = "true"
290+
291+
importCmd.Flags().BoolVarP(&fCollections, "collections", "c", false, "import collections to JSON file")
292+
importCmd.Flags().Lookup("collections").NoOptDefVal = "true"
293+
importCmd.Flags().BoolVarP(&fMetadata, "metadata", "m", false, "import metadata to JSON file")
294+
importCmd.Flags().Lookup("metadata").NoOptDefVal = "true"
295+
importCmd.Flags().BoolVarP(&fExpirationAlerts, "expiration-alerts", "e", false, "import expiration cert alerts to JSON file")
296+
importCmd.Flags().Lookup("expiration-alerts").NoOptDefVal = "true"
297+
importCmd.Flags().BoolVarP(&fIssuedAlerts, "issued-alerts", "i", false, "import issued cert alerts to JSON file")
298+
importCmd.Flags().Lookup("issued-alerts").NoOptDefVal = "true"
299+
importCmd.Flags().BoolVarP(&fDeniedAlerts, "denied-alerts", "d", false, "import denied cert alerts to JSON file")
300+
importCmd.Flags().Lookup("denied-alerts").NoOptDefVal = "true"
301+
importCmd.Flags().BoolVarP(&fPendingAlerts, "pending-alerts", "p", false, "import pending cert alerts to JSON file")
302+
importCmd.Flags().Lookup("pending-alerts").NoOptDefVal = "true"
303+
importCmd.Flags().BoolVarP(&fNetworks, "networks", "n", false, "import SSL networks to JSON file")
304+
importCmd.Flags().Lookup("networks").NoOptDefVal = "true"
305+
importCmd.Flags().BoolVarP(&fWorkflowDefinitions, "workflow-definitions", "w", false, "import workflow definitions to JSON file")
306+
importCmd.Flags().Lookup("workflow-definitions").NoOptDefVal = "true"
307+
importCmd.Flags().BoolVarP(&fReports, "reports", "r", false, "import reports to JSON file")
308+
importCmd.Flags().Lookup("reports").NoOptDefVal = "true"
309+
importCmd.Flags().BoolVarP(&fSecurityRoles, "security-roles", "s", false, "import security roles to JSON file")
310+
importCmd.Flags().Lookup("security-roles").NoOptDefVal = "true"
311+
}

0 commit comments

Comments
 (0)