From d3574f4027ea858282bd5807a0b77c6e0a3bccd0 Mon Sep 17 00:00:00 2001 From: Nicolas PELLEGRIN Date: Sun, 17 Nov 2024 15:08:17 +0100 Subject: [PATCH] feat(iotsitewise): add portal, project, gateway, dashboard, asset, asset model and access policy --- resources/iotsitewise-access-policy.go | 148 +++++++++++++++++++++++++ resources/iotsitewise-asset-model.go | 95 ++++++++++++++++ resources/iotsitewise-asset.go | 146 ++++++++++++++++++++++++ resources/iotsitewise-dashboard.go | 122 ++++++++++++++++++++ resources/iotsitewise-gateway.go | 81 ++++++++++++++ resources/iotsitewise-portal.go | 85 ++++++++++++++ resources/iotsitewise-project.go | 105 ++++++++++++++++++ 7 files changed, 782 insertions(+) create mode 100644 resources/iotsitewise-access-policy.go create mode 100644 resources/iotsitewise-asset-model.go create mode 100644 resources/iotsitewise-asset.go create mode 100644 resources/iotsitewise-dashboard.go create mode 100644 resources/iotsitewise-gateway.go create mode 100644 resources/iotsitewise-portal.go create mode 100644 resources/iotsitewise-project.go diff --git a/resources/iotsitewise-access-policy.go b/resources/iotsitewise-access-policy.go new file mode 100644 index 00000000..b6f58ee9 --- /dev/null +++ b/resources/iotsitewise-access-policy.go @@ -0,0 +1,148 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseAccessPolicyResource = "IoTSiteWiseAccessPolicy" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseAccessPolicyResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseAccessPolicyLister{}, + }) +} + +type IoTSiteWiseAccessPolicyLister struct{} + +func (l *IoTSiteWiseAccessPolicyLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { //nolint:gocyclo + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + // Policies can be attached either to portal or projects + // List portal and portal policies + listPortalsParams := &iotsitewise.ListPortalsInput{ + MaxResults: aws.Int64(25), + } + for { + listPortalsResp, err := svc.ListPortals(listPortalsParams) + if err != nil { + return nil, err + } + for _, portalItem := range listPortalsResp.PortalSummaries { + // Got portals + listProjectsParams := &iotsitewise.ListProjectsInput{ + PortalId: portalItem.Id, + MaxResults: aws.Int64(25), + } + + // List portal policies + listPortalPoliciesParam := &iotsitewise.ListAccessPoliciesInput{ + ResourceId: portalItem.Id, + ResourceType: &([]string{string(iotsitewise.ResourceTypePortal)}[0]), + MaxResults: aws.Int64(25), + } + + for { + listPortalPoliciesResp, err := svc.ListAccessPolicies(listPortalPoliciesParam) + if err != nil { + return nil, err + } + for _, item := range listPortalPoliciesResp.AccessPolicySummaries { + resources = append(resources, &IoTSiteWiseAccessPolicy{ + svc: svc, + ID: item.Id, + }) + } + + if listPortalPoliciesResp.NextToken == nil { + break + } + + listPortalPoliciesParam.NextToken = listPortalPoliciesResp.NextToken + } + + // will also search inside projects + for { + listProjectsResp, err := svc.ListProjects(listProjectsParams) + if err != nil { + return nil, err + } + for _, projectItem := range listProjectsResp.ProjectSummaries { + // List project policies + listProjectPoliciesParams := &iotsitewise.ListAccessPoliciesInput{ + ResourceId: projectItem.Id, + ResourceType: &([]string{string(iotsitewise.ResourceTypeProject)}[0]), + MaxResults: aws.Int64(25), + } + + for { + listProjectPoliciesResp, err := svc.ListAccessPolicies(listProjectPoliciesParams) + if err != nil { + return nil, err + } + for _, item := range listProjectPoliciesResp.AccessPolicySummaries { + resources = append(resources, &IoTSiteWiseAccessPolicy{ + svc: svc, + ID: item.Id, + }) + } + + if listProjectPoliciesResp.NextToken == nil { + break + } + + listProjectPoliciesParams.NextToken = listProjectPoliciesResp.NextToken + } + } + + if listProjectsResp.NextToken == nil { + break + } + + listProjectsParams.NextToken = listProjectsResp.NextToken + } + } + + if listPortalsResp.NextToken == nil { + break + } + + listPortalsParams.NextToken = listPortalsResp.NextToken + } + + return resources, nil +} + +type IoTSiteWiseAccessPolicy struct { + svc *iotsitewise.IoTSiteWise + ID *string +} + +func (r *IoTSiteWiseAccessPolicy) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseAccessPolicy) Remove(_ context.Context) error { + _, err := r.svc.DeleteAccessPolicy(&iotsitewise.DeleteAccessPolicyInput{ + AccessPolicyId: r.ID, + }) + + return err +} + +func (r *IoTSiteWiseAccessPolicy) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-asset-model.go b/resources/iotsitewise-asset-model.go new file mode 100644 index 00000000..70dfb70d --- /dev/null +++ b/resources/iotsitewise-asset-model.go @@ -0,0 +1,95 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseAssetModelResource = "IoTSiteWiseAssetModel" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseAssetModelResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseAssetModelLister{}, + DependsOn: []string{ + IoTSiteWiseAssetResource, + }, + }) +} + +type IoTSiteWiseAssetModelLister struct{} + +func (l *IoTSiteWiseAssetModelLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + params := &iotsitewise.ListAssetModelsInput{ + MaxResults: aws.Int64(25), + } + + for { + resp, err := svc.ListAssetModels(params) + if err != nil { + return nil, err + } + for _, item := range resp.AssetModelSummaries { + tagResp, err := svc.ListTagsForResource( + &iotsitewise.ListTagsForResourceInput{ + ResourceArn: item.Arn, + }) + if err != nil { + return nil, err + } + + resources = append(resources, &IoTSiteWiseAssetModel{ + svc: svc, + ID: item.Id, + Name: item.Name, + Status: item.Status.State, + Tags: tagResp.Tags, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type IoTSiteWiseAssetModel struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string + Status *string + Tags map[string]*string +} + +func (r *IoTSiteWiseAssetModel) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseAssetModel) Remove(_ context.Context) error { + _, err := r.svc.DeleteAssetModel(&iotsitewise.DeleteAssetModelInput{ + AssetModelId: r.ID, + }) + return err +} + +func (r *IoTSiteWiseAssetModel) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-asset.go b/resources/iotsitewise-asset.go new file mode 100644 index 00000000..826a9aa3 --- /dev/null +++ b/resources/iotsitewise-asset.go @@ -0,0 +1,146 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseAssetResource = "IoTSiteWiseAsset" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseAssetResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseAssetLister{}, + }) +} + +type IoTSiteWiseAssetLister struct{} + +func (l *IoTSiteWiseAssetLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + assetModelSummaries, err := ListAssetModels(svc) + if err != nil { + return nil, err + } + + for _, assetModelSummary := range assetModelSummaries { + params := &iotsitewise.ListAssetsInput{ + AssetModelId: assetModelSummary.Id, + MaxResults: aws.Int64(25), + } + + for { + resp, err := svc.ListAssets(params) + if err != nil { + return nil, err + } + for _, item := range resp.AssetSummaries { + tagResp, err := svc.ListTagsForResource( + &iotsitewise.ListTagsForResourceInput{ + ResourceArn: item.Arn, + }) + if err != nil { + return nil, err + } + + resources = append(resources, &IoTSiteWiseAsset{ + svc: svc, + ID: item.Id, + Name: item.Name, + Status: item.Status.State, + Tags: tagResp.Tags, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + } + + return resources, nil +} + +// Utility function to get models, allowing to scan for assets +func ListAssetModels(svc *iotsitewise.IoTSiteWise) ([]*iotsitewise.AssetModelSummary, error) { + resources := make([]*iotsitewise.AssetModelSummary, 0) + params := &iotsitewise.ListAssetModelsInput{ + MaxResults: aws.Int64(25), + } + for { + resp, err := svc.ListAssetModels(params) + if err != nil { + return nil, err + } + resources = append(resources, resp.AssetModelSummaries...) + if resp.NextToken == nil { + break + } + params.NextToken = resp.NextToken + } + return resources, nil +} + +type IoTSiteWiseAsset struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string + Status *string + Tags map[string]*string +} + +func (r *IoTSiteWiseAsset) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseAsset) Remove(_ context.Context) error { + associatedAssets, err := r.svc.ListAssociatedAssets(&iotsitewise.ListAssociatedAssetsInput{ + AssetId: r.ID, + }) + if err != nil { + return err + } + assetDescription, err := r.svc.DescribeAsset(&iotsitewise.DescribeAssetInput{ + AssetId: r.ID, + }) + if err != nil { + return err + } + + // If asset is associated, dissociate before delete + for _, assetHierarchy := range assetDescription.AssetHierarchies { + for _, childAsset := range associatedAssets.AssetSummaries { + // Could fail if hierarchy it not the correct one, ignore it + _, _ = r.svc.DisassociateAssets(&iotsitewise.DisassociateAssetsInput{ + AssetId: r.ID, + ChildAssetId: childAsset.Id, + HierarchyId: assetHierarchy.Id, + }) + } + } + + _, err = r.svc.DeleteAsset(&iotsitewise.DeleteAssetInput{ + AssetId: r.ID, + }) + + return err +} + +func (r *IoTSiteWiseAsset) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-dashboard.go b/resources/iotsitewise-dashboard.go new file mode 100644 index 00000000..4ea04964 --- /dev/null +++ b/resources/iotsitewise-dashboard.go @@ -0,0 +1,122 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseDashboardResource = "IoTSiteWiseDashboard" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseDashboardResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseDashboardLister{}, + }) +} + +type IoTSiteWiseDashboardLister struct{} + +func (l *IoTSiteWiseDashboardLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + // Dashboards can be listed from each project + // To get projects, we must list all portals + listPortalsParams := &iotsitewise.ListPortalsInput{ + MaxResults: aws.Int64(25), + } + for { + listPortalsResp, err := svc.ListPortals(listPortalsParams) + if err != nil { + return nil, err + } + for _, portalItem := range listPortalsResp.PortalSummaries { + // Got portals, will search for projects + listProjectsParams := &iotsitewise.ListProjectsInput{ + PortalId: portalItem.Id, + MaxResults: aws.Int64(25), + } + + for { + listProjectsResp, err := svc.ListProjects(listProjectsParams) + if err != nil { + return nil, err + } + for _, projectItem := range listProjectsResp.ProjectSummaries { + // Got projects, finally get dashboards + listDashboardParams := &iotsitewise.ListDashboardsInput{ + ProjectId: projectItem.Id, + MaxResults: aws.Int64(25), + } + + for { + listDashboardResp, err := svc.ListDashboards(listDashboardParams) + if err != nil { + return nil, err + } + for _, dashboardItem := range listDashboardResp.DashboardSummaries { + resources = append(resources, &IoTSiteWiseDashboard{ + svc: svc, + ID: dashboardItem.Id, + Name: dashboardItem.Name, + }) + } + + if listDashboardResp.NextToken == nil { + break + } + + listDashboardParams.NextToken = listDashboardResp.NextToken + } + } + + if listProjectsResp.NextToken == nil { + break + } + + listProjectsParams.NextToken = listProjectsResp.NextToken + } + } + + if listPortalsResp.NextToken == nil { + break + } + + listPortalsParams.NextToken = listPortalsResp.NextToken + } + + return resources, nil +} + +type IoTSiteWiseDashboard struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string +} + +func (r *IoTSiteWiseDashboard) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseDashboard) Remove(_ context.Context) error { + _, err := r.svc.DeleteDashboard(&iotsitewise.DeleteDashboardInput{ + DashboardId: r.ID, + }) + + return err +} + +func (r *IoTSiteWiseDashboard) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-gateway.go b/resources/iotsitewise-gateway.go new file mode 100644 index 00000000..86c56a6d --- /dev/null +++ b/resources/iotsitewise-gateway.go @@ -0,0 +1,81 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseGatewayResource = "IoTSiteWiseGateway" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseGatewayResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseGatewayLister{}, + }) +} + +type IoTSiteWiseGatewayLister struct{} + +func (l *IoTSiteWiseGatewayLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + params := &iotsitewise.ListGatewaysInput{ + MaxResults: aws.Int64(25), + } + + for { + resp, err := svc.ListGateways(params) + if err != nil { + return nil, err + } + for _, item := range resp.GatewaySummaries { + resources = append(resources, &IoTSiteWiseGateway{ + svc: svc, + ID: item.GatewayId, + Name: item.GatewayName, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type IoTSiteWiseGateway struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string +} + +func (r *IoTSiteWiseGateway) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseGateway) Remove(_ context.Context) error { + _, err := r.svc.DeleteGateway(&iotsitewise.DeleteGatewayInput{ + GatewayId: r.ID, + }) + + return err +} + +func (r *IoTSiteWiseGateway) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-portal.go b/resources/iotsitewise-portal.go new file mode 100644 index 00000000..4049f3f2 --- /dev/null +++ b/resources/iotsitewise-portal.go @@ -0,0 +1,85 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWisePortalResource = "IoTSiteWisePortal" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWisePortalResource, + Scope: nuke.Account, + Lister: &IoTSiteWisePortalLister{}, + DependsOn: []string{ + IoTSiteWiseProjectResource, + IoTSiteWiseAccessPolicyResource, + }, + }) +} + +type IoTSiteWisePortalLister struct{} + +func (l *IoTSiteWisePortalLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + params := &iotsitewise.ListPortalsInput{ + MaxResults: aws.Int64(25), + } + + for { + resp, err := svc.ListPortals(params) + if err != nil { + return nil, err + } + for _, item := range resp.PortalSummaries { + resources = append(resources, &IoTSiteWisePortal{ + svc: svc, + ID: item.Id, + Name: item.Name, + }) + } + + if resp.NextToken == nil { + break + } + + params.NextToken = resp.NextToken + } + + return resources, nil +} + +type IoTSiteWisePortal struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string +} + +func (r *IoTSiteWisePortal) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWisePortal) Remove(_ context.Context) error { + _, err := r.svc.DeletePortal(&iotsitewise.DeletePortalInput{ + PortalId: r.ID, + }) + + return err +} + +func (r *IoTSiteWisePortal) String() string { + return *r.ID +} diff --git a/resources/iotsitewise-project.go b/resources/iotsitewise-project.go new file mode 100644 index 00000000..b0e6c54c --- /dev/null +++ b/resources/iotsitewise-project.go @@ -0,0 +1,105 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iotsitewise" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const IoTSiteWiseProjectResource = "IoTSiteWiseProject" + +func init() { + registry.Register(®istry.Registration{ + Name: IoTSiteWiseProjectResource, + Scope: nuke.Account, + Lister: &IoTSiteWiseProjectLister{}, + DependsOn: []string{ + IoTSiteWiseDashboardResource, + IoTSiteWiseAccessPolicyResource, + }, + }) +} + +type IoTSiteWiseProjectLister struct{} + +func (l *IoTSiteWiseProjectLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := iotsitewise.New(opts.Session) + resources := make([]resource.Resource, 0) + + // To get projects, we must list all portals + listPortalsParams := &iotsitewise.ListPortalsInput{ + MaxResults: aws.Int64(25), + } + for { + listPortalsResp, err := svc.ListPortals(listPortalsParams) + if err != nil { + return nil, err + } + for _, portalItem := range listPortalsResp.PortalSummaries { + // Got portals, will search for projects + listProjectsParams := &iotsitewise.ListProjectsInput{ + PortalId: portalItem.Id, + MaxResults: aws.Int64(25), + } + + for { + listProjectsResp, err := svc.ListProjects(listProjectsParams) + if err != nil { + return nil, err + } + for _, projectItem := range listProjectsResp.ProjectSummaries { + resources = append(resources, &IoTSiteWiseProject{ + svc: svc, + ID: projectItem.Id, + Name: projectItem.Name, + }) + } + + if listProjectsResp.NextToken == nil { + break + } + + listProjectsParams.NextToken = listProjectsResp.NextToken + } + } + + if listPortalsResp.NextToken == nil { + break + } + + listPortalsParams.NextToken = listPortalsResp.NextToken + } + + return resources, nil +} + +type IoTSiteWiseProject struct { + svc *iotsitewise.IoTSiteWise + ID *string + Name *string +} + +func (r *IoTSiteWiseProject) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *IoTSiteWiseProject) Remove(_ context.Context) error { + _, err := r.svc.DeleteProject(&iotsitewise.DeleteProjectInput{ + ProjectId: r.ID, + }) + + return err +} + +func (r *IoTSiteWiseProject) String() string { + return *r.ID +}