diff --git a/apis/iam/generator-config.yaml b/apis/iam/generator-config.yaml index b6a8179147..06e50be00f 100644 --- a/apis/iam/generator-config.yaml +++ b/apis/iam/generator-config.yaml @@ -10,10 +10,15 @@ ignore: - PolicyVersion - Role - SAMLProvider - - ServiceLinkedRole - ServiceSpecificCredential - User - VirtualMFADevice field_paths: - CreateInstanceProfileInput.InstanceProfileName - DeleteInstanceProfileInput.InstanceProfileName +resources: + ServiceLinkedRole: + exceptions: + errors: + 404: + code: NoSuchEntity diff --git a/apis/iam/v1alpha1/custom_types.go b/apis/iam/v1alpha1/custom_types.go index 66a9018519..76a36c4939 100644 --- a/apis/iam/v1alpha1/custom_types.go +++ b/apis/iam/v1alpha1/custom_types.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package v1alpha1 import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" @@ -17,3 +33,6 @@ type CustomInstanceProfileParameters struct { // +optional RoleSelector *xpv1.Selector `json:"roleSelector,omitempty"` } + +// CustomServiceLinkedRoleParameters includes the custom fields of ServiceLinkedRole. +type CustomServiceLinkedRoleParameters struct{} diff --git a/apis/iam/v1alpha1/zz_generated.deepcopy.go b/apis/iam/v1alpha1/zz_generated.deepcopy.go index 12f6f2da05..af66ef4d63 100644 --- a/apis/iam/v1alpha1/zz_generated.deepcopy.go +++ b/apis/iam/v1alpha1/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ limitations under the License. package v1alpha1 import ( - "github.com/crossplane/crossplane-runtime/apis/common/v1" + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -192,6 +192,21 @@ func (in *CustomInstanceProfileParameters) DeepCopy() *CustomInstanceProfilePara return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomServiceLinkedRoleParameters) DeepCopyInto(out *CustomServiceLinkedRoleParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomServiceLinkedRoleParameters. +func (in *CustomServiceLinkedRoleParameters) DeepCopy() *CustomServiceLinkedRoleParameters { + if in == nil { + return nil + } + out := new(CustomServiceLinkedRoleParameters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EntityDetails) DeepCopyInto(out *EntityDetails) { *out = *in @@ -283,6 +298,11 @@ func (in *Group) DeepCopyInto(out *Group) { *out = new(string) **out = **in } + if in.GroupName != nil { + in, out := &in.GroupName, &out.GroupName + *out = new(string) + **out = **in + } if in.Path != nil { in, out := &in.Path, &out.Path *out = new(string) @@ -317,6 +337,11 @@ func (in *GroupDetail) DeepCopyInto(out *GroupDetail) { *out = new(string) **out = **in } + if in.GroupName != nil { + in, out := &in.GroupName, &out.GroupName + *out = new(string) + **out = **in + } if in.Path != nil { in, out := &in.Path, &out.Path *out = new(string) @@ -848,6 +873,11 @@ func (in *PolicyGroup) DeepCopyInto(out *PolicyGroup) { *out = new(string) **out = **in } + if in.GroupName != nil { + in, out := &in.GroupName, &out.GroupName + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyGroup. @@ -1268,6 +1298,200 @@ func (in *ServiceLastAccessed) DeepCopy() *ServiceLastAccessed { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRole) DeepCopyInto(out *ServiceLinkedRole) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRole. +func (in *ServiceLinkedRole) DeepCopy() *ServiceLinkedRole { + if in == nil { + return nil + } + out := new(ServiceLinkedRole) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceLinkedRole) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRoleList) DeepCopyInto(out *ServiceLinkedRoleList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ServiceLinkedRole, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRoleList. +func (in *ServiceLinkedRoleList) DeepCopy() *ServiceLinkedRoleList { + if in == nil { + return nil + } + out := new(ServiceLinkedRoleList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ServiceLinkedRoleList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRoleObservation) DeepCopyInto(out *ServiceLinkedRoleObservation) { + *out = *in + if in.ARN != nil { + in, out := &in.ARN, &out.ARN + *out = new(string) + **out = **in + } + if in.AssumeRolePolicyDocument != nil { + in, out := &in.AssumeRolePolicyDocument, &out.AssumeRolePolicyDocument + *out = new(string) + **out = **in + } + if in.CreateDate != nil { + in, out := &in.CreateDate, &out.CreateDate + *out = (*in).DeepCopy() + } + if in.MaxSessionDuration != nil { + in, out := &in.MaxSessionDuration, &out.MaxSessionDuration + *out = new(int64) + **out = **in + } + if in.Path != nil { + in, out := &in.Path, &out.Path + *out = new(string) + **out = **in + } + if in.PermissionsBoundary != nil { + in, out := &in.PermissionsBoundary, &out.PermissionsBoundary + *out = new(AttachedPermissionsBoundary) + (*in).DeepCopyInto(*out) + } + if in.RoleID != nil { + in, out := &in.RoleID, &out.RoleID + *out = new(string) + **out = **in + } + if in.RoleLastUsed != nil { + in, out := &in.RoleLastUsed, &out.RoleLastUsed + *out = new(RoleLastUsed) + (*in).DeepCopyInto(*out) + } + if in.RoleName != nil { + in, out := &in.RoleName, &out.RoleName + *out = new(string) + **out = **in + } + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]*Tag, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(Tag) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRoleObservation. +func (in *ServiceLinkedRoleObservation) DeepCopy() *ServiceLinkedRoleObservation { + if in == nil { + return nil + } + out := new(ServiceLinkedRoleObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRoleParameters) DeepCopyInto(out *ServiceLinkedRoleParameters) { + *out = *in + if in.AWSServiceName != nil { + in, out := &in.AWSServiceName, &out.AWSServiceName + *out = new(string) + **out = **in + } + if in.CustomSuffix != nil { + in, out := &in.CustomSuffix, &out.CustomSuffix + *out = new(string) + **out = **in + } + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + out.CustomServiceLinkedRoleParameters = in.CustomServiceLinkedRoleParameters +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRoleParameters. +func (in *ServiceLinkedRoleParameters) DeepCopy() *ServiceLinkedRoleParameters { + if in == nil { + return nil + } + out := new(ServiceLinkedRoleParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRoleSpec) DeepCopyInto(out *ServiceLinkedRoleSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRoleSpec. +func (in *ServiceLinkedRoleSpec) DeepCopy() *ServiceLinkedRoleSpec { + if in == nil { + return nil + } + out := new(ServiceLinkedRoleSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ServiceLinkedRoleStatus) DeepCopyInto(out *ServiceLinkedRoleStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceLinkedRoleStatus. +func (in *ServiceLinkedRoleStatus) DeepCopy() *ServiceLinkedRoleStatus { + if in == nil { + return nil + } + out := new(ServiceLinkedRoleStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceSpecificCredential) DeepCopyInto(out *ServiceSpecificCredential) { *out = *in diff --git a/apis/iam/v1alpha1/zz_generated.managed.go b/apis/iam/v1alpha1/zz_generated.managed.go index 1586a5d1af..a98ad6d726 100644 --- a/apis/iam/v1alpha1/zz_generated.managed.go +++ b/apis/iam/v1alpha1/zz_generated.managed.go @@ -85,3 +85,69 @@ func (mg *InstanceProfile) SetPublishConnectionDetailsTo(r *xpv1.PublishConnecti func (mg *InstanceProfile) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { mg.Spec.WriteConnectionSecretToReference = r } + +// GetCondition of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetProviderConfigReference of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +/* +GetProviderReference of this ServiceLinkedRole. +Deprecated: Use GetProviderConfigReference. +*/ +func (mg *ServiceLinkedRole) GetProviderReference() *xpv1.Reference { + return mg.Spec.ProviderReference +} + +// GetPublishConnectionDetailsTo of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetProviderConfigReference of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +/* +SetProviderReference of this ServiceLinkedRole. +Deprecated: Use SetProviderConfigReference. +*/ +func (mg *ServiceLinkedRole) SetProviderReference(r *xpv1.Reference) { + mg.Spec.ProviderReference = r +} + +// SetPublishConnectionDetailsTo of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this ServiceLinkedRole. +func (mg *ServiceLinkedRole) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/iam/v1alpha1/zz_generated.managedlist.go b/apis/iam/v1alpha1/zz_generated.managedlist.go index 776f76656d..d4ce6f67dd 100644 --- a/apis/iam/v1alpha1/zz_generated.managedlist.go +++ b/apis/iam/v1alpha1/zz_generated.managedlist.go @@ -28,3 +28,12 @@ func (l *InstanceProfileList) GetItems() []resource.Managed { } return items } + +// GetItems of this ServiceLinkedRoleList. +func (l *ServiceLinkedRoleList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/apis/iam/v1alpha1/zz_generated.resolvers.go b/apis/iam/v1alpha1/zz_generated.resolvers.go index d1a5a26a7c..3af6f7f2f6 100644 --- a/apis/iam/v1alpha1/zz_generated.resolvers.go +++ b/apis/iam/v1alpha1/zz_generated.resolvers.go @@ -20,6 +20,7 @@ package v1alpha1 import ( "context" + v1beta1 "github.com/crossplane-contrib/provider-aws/apis/iam/v1beta1" reference "github.com/crossplane/crossplane-runtime/pkg/reference" errors "github.com/pkg/errors" diff --git a/apis/iam/v1alpha1/zz_service_linked_role.go b/apis/iam/v1alpha1/zz_service_linked_role.go new file mode 100644 index 0000000000..d2d347b9fb --- /dev/null +++ b/apis/iam/v1alpha1/zz_service_linked_role.go @@ -0,0 +1,149 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package v1alpha1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// ServiceLinkedRoleParameters defines the desired state of ServiceLinkedRole +type ServiceLinkedRoleParameters struct { + // The service principal for the Amazon Web Services service to which this role + // is attached. You use a string similar to a URL but without the http:// in + // front. For example: elasticbeanstalk.amazonaws.com. + // + // Service principals are unique and case-sensitive. To find the exact service + // principal for your service-linked role, see Amazon Web Services services + // that work with IAM (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) + // in the IAM User Guide. Look for the services that have Yes in the Service-Linked + // Role column. Choose the Yes link to view the service-linked role documentation + // for that service. + // +kubebuilder:validation:Required + AWSServiceName *string `json:"awsServiceName"` + // A string that you provide, which is combined with the service-provided prefix + // to form the complete role name. If you make multiple requests for the same + // service, then you must supply a different CustomSuffix for each request. + // Otherwise the request fails with a duplicate role name error. For example, + // you could add -1 or -debug to the suffix. + // + // Some services do not support the CustomSuffix parameter. If you provide an + // optional suffix and the operation fails, try the operation again without + // the suffix. + CustomSuffix *string `json:"customSuffix,omitempty"` + // The description of the role. + Description *string `json:"description,omitempty"` + CustomServiceLinkedRoleParameters `json:",inline"` +} + +// ServiceLinkedRoleSpec defines the desired state of ServiceLinkedRole +type ServiceLinkedRoleSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider ServiceLinkedRoleParameters `json:"forProvider"` +} + +// ServiceLinkedRoleObservation defines the observed state of ServiceLinkedRole +type ServiceLinkedRoleObservation struct { + // The Amazon Resource Name (ARN) specifying the role. For more information + // about ARNs and how to use them in policies, see IAM identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + // in the IAM User Guide guide. + ARN *string `json:"arn,omitempty"` + // The policy that grants an entity permission to assume the role. + AssumeRolePolicyDocument *string `json:"assumeRolePolicyDocument,omitempty"` + // The date and time, in ISO 8601 date-time format (http://www.iso.org/iso/iso8601), + // when the role was created. + CreateDate *metav1.Time `json:"createDate,omitempty"` + // The maximum session duration (in seconds) for the specified role. Anyone + // who uses the CLI, or API to assume the role can specify the duration using + // the optional DurationSeconds API parameter or duration-seconds CLI parameter. + MaxSessionDuration *int64 `json:"maxSessionDuration,omitempty"` + // The path to the role. For more information about paths, see IAM identifiers + // (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + // in the IAM User Guide. + Path *string `json:"path,omitempty"` + // The ARN of the policy used to set the permissions boundary for the role. + // + // For more information about permissions boundaries, see Permissions boundaries + // for IAM identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) + // in the IAM User Guide. + PermissionsBoundary *AttachedPermissionsBoundary `json:"permissionsBoundary,omitempty"` + // The stable and unique string identifying the role. For more information about + // IDs, see IAM identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + // in the IAM User Guide. + RoleID *string `json:"roleID,omitempty"` + // Contains information about the last time that an IAM role was used. This + // includes the date and time and the Region in which the role was last used. + // Activity is only reported for the trailing 400 days. This period can be shorter + // if your Region began supporting these features within the last year. The + // role might have been used more than 400 days ago. For more information, see + // Regions where data is tracked (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_access-advisor.html#access-advisor_tracking-period) + // in the IAM User Guide. + RoleLastUsed *RoleLastUsed `json:"roleLastUsed,omitempty"` + // The friendly name that identifies the role. + RoleName *string `json:"roleName,omitempty"` + // A list of tags that are attached to the role. For more information about + // tagging, see Tagging IAM resources (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) + // in the IAM User Guide. + Tags []*Tag `json:"tags,omitempty"` +} + +// ServiceLinkedRoleStatus defines the observed state of ServiceLinkedRole. +type ServiceLinkedRoleStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider ServiceLinkedRoleObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// ServiceLinkedRole is the Schema for the ServiceLinkedRoles API +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="EXTERNAL-NAME",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,aws} +type ServiceLinkedRole struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ServiceLinkedRoleSpec `json:"spec"` + Status ServiceLinkedRoleStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ServiceLinkedRoleList contains a list of ServiceLinkedRoles +type ServiceLinkedRoleList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ServiceLinkedRole `json:"items"` +} + +// Repository type metadata. +var ( + ServiceLinkedRoleKind = "ServiceLinkedRole" + ServiceLinkedRoleGroupKind = schema.GroupKind{Group: CRDGroup, Kind: ServiceLinkedRoleKind}.String() + ServiceLinkedRoleKindAPIVersion = ServiceLinkedRoleKind + "." + GroupVersion.String() + ServiceLinkedRoleGroupVersionKind = GroupVersion.WithKind(ServiceLinkedRoleKind) +) + +func init() { + SchemeBuilder.Register(&ServiceLinkedRole{}, &ServiceLinkedRoleList{}) +} diff --git a/apis/iam/v1alpha1/zz_types.go b/apis/iam/v1alpha1/zz_types.go index 4fa826a7fa..0a4205c836 100644 --- a/apis/iam/v1alpha1/zz_types.go +++ b/apis/iam/v1alpha1/zz_types.go @@ -114,6 +114,8 @@ type Group struct { GroupID *string `json:"groupID,omitempty"` + GroupName *string `json:"groupName,omitempty"` + Path *string `json:"path,omitempty"` } @@ -130,6 +132,8 @@ type GroupDetail struct { GroupID *string `json:"groupID,omitempty"` + GroupName *string `json:"groupName,omitempty"` + Path *string `json:"path,omitempty"` } @@ -260,6 +264,8 @@ type PolicyGrantingServiceAccess struct { // +kubebuilder:skipversion type PolicyGroup struct { GroupID *string `json:"groupID,omitempty"` + + GroupName *string `json:"groupName,omitempty"` } // +kubebuilder:skipversion diff --git a/apis/iam/v1beta1/zz_generated.deepcopy.go b/apis/iam/v1beta1/zz_generated.deepcopy.go index 3c22cdec2d..2f57190964 100644 --- a/apis/iam/v1beta1/zz_generated.deepcopy.go +++ b/apis/iam/v1beta1/zz_generated.deepcopy.go @@ -22,7 +22,7 @@ limitations under the License. package v1beta1 import ( - "github.com/crossplane/crossplane-runtime/apis/common/v1" + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/apis/iam/v1beta1/zz_generated.resolvers.go b/apis/iam/v1beta1/zz_generated.resolvers.go index 82336da934..7f70f2fdac 100644 --- a/apis/iam/v1beta1/zz_generated.resolvers.go +++ b/apis/iam/v1beta1/zz_generated.resolvers.go @@ -20,6 +20,7 @@ package v1beta1 import ( "context" + reference "github.com/crossplane/crossplane-runtime/pkg/reference" errors "github.com/pkg/errors" client "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/examples/iam/servicelinkedrole.yaml b/examples/iam/servicelinkedrole.yaml new file mode 100644 index 0000000000..091359e1eb --- /dev/null +++ b/examples/iam/servicelinkedrole.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: iam.aws.crossplane.io/v1alpha1 +kind: ServiceLinkedRole +metadata: + name: slr-elasticbeanstalk +spec: + forProvider: + awsServiceName: elasticbeanstalk.amazonaws.com + providerConfigRef: + name: example +--- +apiVersion: iam.aws.crossplane.io/v1alpha1 +kind: ServiceLinkedRole +metadata: + annotations: + crossplane.io/external-name: AWSServiceRoleForRDS + name: slr-rds +spec: + forProvider: + awsServiceName: rds.amazonaws.com + providerConfigRef: + name: example diff --git a/package/crds/iam.aws.crossplane.io_servicelinkedroles.yaml b/package/crds/iam.aws.crossplane.io_servicelinkedroles.yaml new file mode 100644 index 0000000000..817d5ace9e --- /dev/null +++ b/package/crds/iam.aws.crossplane.io_servicelinkedroles.yaml @@ -0,0 +1,394 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: servicelinkedroles.iam.aws.crossplane.io +spec: + group: iam.aws.crossplane.io + names: + categories: + - crossplane + - managed + - aws + kind: ServiceLinkedRole + listKind: ServiceLinkedRoleList + plural: servicelinkedroles + singular: servicelinkedrole + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .metadata.annotations.crossplane\.io/external-name + name: EXTERNAL-NAME + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ServiceLinkedRole is the Schema for the ServiceLinkedRoles API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ServiceLinkedRoleSpec defines the desired state of ServiceLinkedRole + properties: + deletionPolicy: + default: Delete + description: DeletionPolicy specifies what will happen to the underlying + external when this managed resource is deleted - either "Delete" + or "Orphan" the external resource. + enum: + - Orphan + - Delete + type: string + forProvider: + description: ServiceLinkedRoleParameters defines the desired state + of ServiceLinkedRole + properties: + awsServiceName: + description: "The service principal for the Amazon Web Services + service to which this role is attached. You use a string similar + to a URL but without the http:// in front. For example: elasticbeanstalk.amazonaws.com. + \n Service principals are unique and case-sensitive. To find + the exact service principal for your service-linked role, see + Amazon Web Services services that work with IAM (https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html) + in the IAM User Guide. Look for the services that have Yes in + the Service-Linked Role column. Choose the Yes link to view + the service-linked role documentation for that service." + type: string + customSuffix: + description: "A string that you provide, which is combined with + the service-provided prefix to form the complete role name. + If you make multiple requests for the same service, then you + must supply a different CustomSuffix for each request. Otherwise + the request fails with a duplicate role name error. For example, + you could add -1 or -debug to the suffix. \n Some services do + not support the CustomSuffix parameter. If you provide an optional + suffix and the operation fails, try the operation again without + the suffix." + type: string + description: + description: The description of the role. + type: string + required: + - awsServiceName + type: object + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that + will be used to create, observe, update, and delete this managed + resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be + used to create, observe, update, and delete this managed resource. + Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: PublishConnectionDetailsTo specifies the connection secret + config which contains a name, metadata and a reference to secret + store config to which any connection details for this managed resource + should be written. Connection details frequently include the endpoint, + username, and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: SecretStoreConfigRef specifies which secret store + config should be used for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of + this reference is required. The default is 'Required', + which means the reconcile will fail if the reference + cannot be resolved. 'Optional' means this reference + will be a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will + attempt to resolve the reference only when the corresponding + field is not present. Use 'Always' to resolve the reference + on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are the annotations to be added to + connection secret. - For Kubernetes secrets, this will be + used as "metadata.annotations". - It is up to Secret Store + implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: Labels are the labels/tags to be added to connection + secret. - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store + types. + type: object + type: + description: Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the + managed resource. This field is planned to be replaced in a future + release in favor of PublishConnectionDetailsTo. Currently, both + could be set independently and connection details would be published + to both without affecting each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: ServiceLinkedRoleStatus defines the observed state of ServiceLinkedRole. + properties: + atProvider: + description: ServiceLinkedRoleObservation defines the observed state + of ServiceLinkedRole + properties: + arn: + description: The Amazon Resource Name (ARN) specifying the role. + For more information about ARNs and how to use them in policies, + see IAM identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + in the IAM User Guide guide. + type: string + assumeRolePolicyDocument: + description: The policy that grants an entity permission to assume + the role. + type: string + createDate: + description: The date and time, in ISO 8601 date-time format (http://www.iso.org/iso/iso8601), + when the role was created. + format: date-time + type: string + maxSessionDuration: + description: The maximum session duration (in seconds) for the + specified role. Anyone who uses the CLI, or API to assume the + role can specify the duration using the optional DurationSeconds + API parameter or duration-seconds CLI parameter. + format: int64 + type: integer + path: + description: The path to the role. For more information about + paths, see IAM identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + in the IAM User Guide. + type: string + permissionsBoundary: + description: "The ARN of the policy used to set the permissions + boundary for the role. \n For more information about permissions + boundaries, see Permissions boundaries for IAM identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) + in the IAM User Guide." + properties: + permissionsBoundaryARN: + description: "The Amazon Resource Name (ARN). ARNs are unique + identifiers for Amazon Web Services resources. \n For more + information about ARNs, go to Amazon Resource Names (ARNs) + (https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) + in the Amazon Web Services General Reference." + type: string + permissionsBoundaryType: + type: string + type: object + roleID: + description: The stable and unique string identifying the role. + For more information about IDs, see IAM identifiers (https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html) + in the IAM User Guide. + type: string + roleLastUsed: + description: Contains information about the last time that an + IAM role was used. This includes the date and time and the Region + in which the role was last used. Activity is only reported for + the trailing 400 days. This period can be shorter if your Region + began supporting these features within the last year. The role + might have been used more than 400 days ago. For more information, + see Regions where data is tracked (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_access-advisor.html#access-advisor_tracking-period) + in the IAM User Guide. + properties: + lastUsedDate: + format: date-time + type: string + region: + type: string + type: object + roleName: + description: The friendly name that identifies the role. + type: string + tags: + description: A list of tags that are attached to the role. For + more information about tagging, see Tagging IAM resources (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html) + in the IAM User Guide. + items: + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/pkg/clients/aws.go b/pkg/clients/aws.go index 9e0d501a10..cd0283c968 100644 --- a/pkg/clients/aws.go +++ b/pkg/clients/aws.go @@ -885,6 +885,14 @@ func Int32Value(v *int32) int32 { return 0 } +// TimeToMetaTime converts a standard Go time.Time to a K8s metav1.Time. +func TimeToMetaTime(t *time.Time) *metav1.Time { + if t == nil { + return nil + } + return &metav1.Time{Time: *t} +} + // LateInitializeStringPtr returns in if it's non-nil, otherwise returns from // which is the backup for the cases in is nil. func LateInitializeStringPtr(in *string, from *string) *string { diff --git a/pkg/clients/aws_arn.go b/pkg/clients/aws_arn.go new file mode 100644 index 0000000000..ae30024a67 --- /dev/null +++ b/pkg/clients/aws_arn.go @@ -0,0 +1,57 @@ +/* +Copyright 2023 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + "fmt" + "regexp" + + "github.com/pkg/errors" +) + +// ARN represents a +type ARN struct { + Partition string + Service string + Region string + AccountID string + Resource string +} + +// String returns the string representation of a. +func (a *ARN) String() string { + return fmt.Sprintf("arn:%s:%s:%s:%s:%s", a.Partition, a.Service, a.Region, a.AccountID, a.Resource) +} + +var ( + arnRegex = regexp.MustCompile(`^arn:([\w\d-]*):([\w\d-]*):([\w\d-]*):([\w\d-]*):(.*)$`) +) + +// ParseARN extract ARN information from s. +func ParseARN(s string) (ARN, error) { + match := arnRegex.FindStringSubmatch(s) + if match == nil { + return ARN{}, errors.Errorf("%q is not a valid ARN", s) + } + return ARN{ + Partition: match[1], + Service: match[2], + Region: match[3], + AccountID: match[4], + Resource: match[5], + }, nil +} diff --git a/pkg/clients/aws_arn_test.go b/pkg/clients/aws_arn_test.go new file mode 100644 index 0000000000..199964cc3a --- /dev/null +++ b/pkg/clients/aws_arn_test.go @@ -0,0 +1,93 @@ +/* +Copyright 2019 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package aws + +import ( + "testing" + + "github.com/crossplane/crossplane-runtime/pkg/test" + "github.com/google/go-cmp/cmp" +) + +func TestParseARN(t *testing.T) { + type args struct { + arn string + } + type want struct { + arn ARN + err error + } + cases := map[string]struct { + args args + want want + }{ + "WithWildcard": { + args: args{ + arn: "arn:aws:iam::123456789012:user/Development/product_1234/*", + }, + want: want{ + arn: ARN{ + Partition: "aws", + Service: "iam", + Region: "", + AccountID: "123456789012", + Resource: "user/Development/product_1234/*", + }, + }, + }, + "WithDots": { + args: args{ + arn: "arn:aws:iam::123456789012:role/aws-service-role/access-analyzer.amazonaws.com/AWSServiceRoleForAccessAnalyzer", + }, + want: want{ + arn: ARN{ + Partition: "aws", + Service: "iam", + Region: "", + AccountID: "123456789012", + Resource: "role/aws-service-role/access-analyzer.amazonaws.com/AWSServiceRoleForAccessAnalyzer", + }, + }, + }, + "WithSpaces": { + args: args{ + arn: "arn:aws:iam::123456789012:u2f/user/JohnDoe/default (U2F security key)", + }, + want: want{ + arn: ARN{ + Partition: "aws", + Service: "iam", + Region: "", + AccountID: "123456789012", + Resource: "u2f/user/JohnDoe/default (U2F security key)", + }, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + arn, err := ParseARN(tc.args.arn) + if diff := cmp.Diff(tc.want.arn, arn); diff != "" { + t.Errorf("Wrap: -want, +got:\n%s", diff) + } + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("Wrap: -want, +got:\n%s", diff) + } + }) + } +} diff --git a/pkg/controller/aws.go b/pkg/controller/aws.go index 8c96da40b6..d7fa433026 100644 --- a/pkg/controller/aws.go +++ b/pkg/controller/aws.go @@ -135,6 +135,7 @@ import ( "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/policy" "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/role" "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/rolepolicyattachment" + "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/servicelinkedrole" "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/user" "github.com/crossplane-contrib/provider-aws/pkg/controller/iam/userpolicyattachment" iotpolicy "github.com/crossplane-contrib/provider-aws/pkg/controller/iot/policy" @@ -356,6 +357,7 @@ func Setup(mgr ctrl.Manager, o controller.Options) error { autoscalinggroup.SetupAutoScalingGroup, servicediscoveryservice.SetupService, configurationset.SetupConfigurationSet, + servicelinkedrole.SetupServiceLinkedRole, emailidentity.SetupEmailIdentity, emailtemplate.SetupEmailTemplate, } { diff --git a/pkg/controller/iam/servicelinkedrole/setup.go b/pkg/controller/iam/servicelinkedrole/setup.go new file mode 100644 index 0000000000..2dd04ffbfa --- /dev/null +++ b/pkg/controller/iam/servicelinkedrole/setup.go @@ -0,0 +1,168 @@ +/* +Copyright 2022 The Crossplane Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package servicelinkedrole + +import ( + "context" + "strings" + + svcsdk "github.com/aws/aws-sdk-go/service/iam" + svcsdkapi "github.com/aws/aws-sdk-go/service/iam/iamiface" + "github.com/pkg/errors" + ctrl "sigs.k8s.io/controller-runtime" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/iam/v1alpha1" + "github.com/crossplane-contrib/provider-aws/apis/v1alpha1" + awsclients "github.com/crossplane-contrib/provider-aws/pkg/clients" + "github.com/crossplane-contrib/provider-aws/pkg/features" +) + +const ( + errNotServiceLinkedRole = "role is not a service-linked-role" + errGetRole = "cannot get role" +) + +// SetupServiceLinkedRole adds a controller that reconciles ServiceLinkedRole. +func SetupServiceLinkedRole(mgr ctrl.Manager, o controller.Options) error { + name := managed.ControllerName(svcapitypes.ServiceLinkedRoleGroupKind) + opts := []option{ + func(e *external) { + h := hooks{client: e.client} + e.postCreate = postCreate + e.preDelete = preDelete + e.observe = h.observe + }, + } + + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.Features.Enabled(features.EnableAlphaExternalSecretStores) { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), v1alpha1.StoreConfigGroupVersionKind)) + } + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(resource.DesiredStateChanged()). + For(&svcapitypes.ServiceLinkedRole{}). + Complete(managed.NewReconciler(mgr, + resource.ManagedKind(svcapitypes.ServiceLinkedRoleGroupVersionKind), + managed.WithExternalConnecter(&connector{kube: mgr.GetClient(), opts: opts}), + managed.WithConnectionPublishers(), + managed.WithPollInterval(o.PollInterval), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithConnectionPublishers(cps...))) +} + +type hooks struct { + client svcsdkapi.IAMAPI +} + +func (e *hooks) observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + cr, ok := mg.(*svcapitypes.ServiceLinkedRole) + if !ok { + return managed.ExternalObservation{}, errors.New(errUnexpectedObject) + } + + if meta.GetExternalName(cr) == "" { + return managed.ExternalObservation{}, nil + } + + res, err := e.client.GetRoleWithContext(ctx, &svcsdk.GetRoleInput{ + RoleName: awsclients.String(meta.GetExternalName(cr)), + }) + if err != nil { + return managed.ExternalObservation{}, errors.Wrap(resource.Ignore(IsNotFound, err), errGetRole) + } + + cr.Status.AtProvider = generateServiceLinkedRoleObservation(res.Role) + + if err := isServiceLinkedRole(res.Role); err != nil { + return managed.ExternalObservation{ + ResourceExists: true, + }, err + } + + cr.SetConditions(xpv1.Available()) + return managed.ExternalObservation{ + ResourceExists: true, + ResourceUpToDate: true, + }, nil +} + +func isServiceLinkedRole(role *svcsdk.Role) error { + arn, err := awsclients.ParseARN(awsclients.StringValue(role.Arn)) + if err != nil { + return err + } + if !strings.HasPrefix(arn.Resource, "role/aws-service-role/") { + return errors.New(errNotServiceLinkedRole) + } + return nil +} + +func generateServiceLinkedRoleObservation(role *svcsdk.Role) svcapitypes.ServiceLinkedRoleObservation { + o := svcapitypes.ServiceLinkedRoleObservation{ + ARN: role.Arn, + AssumeRolePolicyDocument: role.AssumeRolePolicyDocument, + CreateDate: awsclients.TimeToMetaTime(role.CreateDate), + MaxSessionDuration: role.MaxSessionDuration, + Path: role.Path, + RoleID: role.RoleId, + RoleName: role.RoleName, + } + if role.PermissionsBoundary != nil { + o.PermissionsBoundary = &svcapitypes.AttachedPermissionsBoundary{ + PermissionsBoundaryARN: role.PermissionsBoundary.PermissionsBoundaryArn, + PermissionsBoundaryType: role.PermissionsBoundary.PermissionsBoundaryType, + } + } + if role.RoleLastUsed != nil { + o.RoleLastUsed = &svcapitypes.RoleLastUsed{ + LastUsedDate: awsclients.TimeToMetaTime(role.RoleLastUsed.LastUsedDate), + Region: role.RoleLastUsed.Region, + } + } + o.Tags = make([]*svcapitypes.Tag, len(role.Tags)) + for i, t := range role.Tags { + o.Tags[i] = &svcapitypes.Tag{ + Key: t.Key, + Value: t.Value, + } + } + return o +} + +func postCreate(ctx context.Context, cr *svcapitypes.ServiceLinkedRole, obj *svcsdk.CreateServiceLinkedRoleOutput, cre managed.ExternalCreation, err error) (managed.ExternalCreation, error) { + if err != nil { + return managed.ExternalCreation{}, err + } + + nn := strings.Split(awsclients.StringValue(obj.Role.Arn), "/") + meta.SetExternalName(cr, nn[3]) + return managed.ExternalCreation{}, nil +} + +func preDelete(ctx context.Context, cr *svcapitypes.ServiceLinkedRole, obj *svcsdk.DeleteServiceLinkedRoleInput) (bool, error) { + obj.RoleName = awsclients.String(meta.GetExternalName(cr)) + return false, nil +} diff --git a/pkg/controller/iam/servicelinkedrole/zz_controller.go b/pkg/controller/iam/servicelinkedrole/zz_controller.go new file mode 100644 index 0000000000..efe144edac --- /dev/null +++ b/pkg/controller/iam/servicelinkedrole/zz_controller.go @@ -0,0 +1,236 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package servicelinkedrole + +import ( + "context" + + svcapi "github.com/aws/aws-sdk-go/service/iam" + svcsdk "github.com/aws/aws-sdk-go/service/iam" + svcsdkapi "github.com/aws/aws-sdk-go/service/iam/iamiface" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + cpresource "github.com/crossplane/crossplane-runtime/pkg/resource" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/iam/v1alpha1" + awsclient "github.com/crossplane-contrib/provider-aws/pkg/clients" +) + +const ( + errUnexpectedObject = "managed resource is not an ServiceLinkedRole resource" + + errCreateSession = "cannot create a new session" + errCreate = "cannot create ServiceLinkedRole in AWS" + errUpdate = "cannot update ServiceLinkedRole in AWS" + errDescribe = "failed to describe ServiceLinkedRole" + errDelete = "failed to delete ServiceLinkedRole" +) + +type connector struct { + kube client.Client + opts []option +} + +func (c *connector) Connect(ctx context.Context, mg cpresource.Managed) (managed.ExternalClient, error) { + sess, err := awsclient.GetConfigV1(ctx, c.kube, mg, awsclient.GlobalRegion) + if err != nil { + return nil, errors.Wrap(err, errCreateSession) + } + return newExternal(c.kube, svcapi.New(sess), c.opts), nil +} + +func (e *external) Observe(ctx context.Context, mg cpresource.Managed) (managed.ExternalObservation, error) { + return e.observe(ctx, mg) +} + +func (e *external) Create(ctx context.Context, mg cpresource.Managed) (managed.ExternalCreation, error) { + cr, ok := mg.(*svcapitypes.ServiceLinkedRole) + if !ok { + return managed.ExternalCreation{}, errors.New(errUnexpectedObject) + } + cr.Status.SetConditions(xpv1.Creating()) + input := GenerateCreateServiceLinkedRoleInput(cr) + if err := e.preCreate(ctx, cr, input); err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, "pre-create failed") + } + resp, err := e.client.CreateServiceLinkedRoleWithContext(ctx, input) + if err != nil { + return managed.ExternalCreation{}, awsclient.Wrap(err, errCreate) + } + + if resp.Role.Arn != nil { + cr.Status.AtProvider.ARN = resp.Role.Arn + } else { + cr.Status.AtProvider.ARN = nil + } + if resp.Role.AssumeRolePolicyDocument != nil { + cr.Status.AtProvider.AssumeRolePolicyDocument = resp.Role.AssumeRolePolicyDocument + } else { + cr.Status.AtProvider.AssumeRolePolicyDocument = nil + } + if resp.Role.CreateDate != nil { + cr.Status.AtProvider.CreateDate = &metav1.Time{*resp.Role.CreateDate} + } else { + cr.Status.AtProvider.CreateDate = nil + } + if resp.Role.Description != nil { + cr.Spec.ForProvider.Description = resp.Role.Description + } else { + cr.Spec.ForProvider.Description = nil + } + if resp.Role.MaxSessionDuration != nil { + cr.Status.AtProvider.MaxSessionDuration = resp.Role.MaxSessionDuration + } else { + cr.Status.AtProvider.MaxSessionDuration = nil + } + if resp.Role.Path != nil { + cr.Status.AtProvider.Path = resp.Role.Path + } else { + cr.Status.AtProvider.Path = nil + } + if resp.Role.PermissionsBoundary != nil { + f6 := &svcapitypes.AttachedPermissionsBoundary{} + if resp.Role.PermissionsBoundary.PermissionsBoundaryArn != nil { + f6.PermissionsBoundaryARN = resp.Role.PermissionsBoundary.PermissionsBoundaryArn + } + if resp.Role.PermissionsBoundary.PermissionsBoundaryType != nil { + f6.PermissionsBoundaryType = resp.Role.PermissionsBoundary.PermissionsBoundaryType + } + cr.Status.AtProvider.PermissionsBoundary = f6 + } else { + cr.Status.AtProvider.PermissionsBoundary = nil + } + if resp.Role.RoleId != nil { + cr.Status.AtProvider.RoleID = resp.Role.RoleId + } else { + cr.Status.AtProvider.RoleID = nil + } + if resp.Role.RoleLastUsed != nil { + f8 := &svcapitypes.RoleLastUsed{} + if resp.Role.RoleLastUsed.LastUsedDate != nil { + f8.LastUsedDate = &metav1.Time{*resp.Role.RoleLastUsed.LastUsedDate} + } + if resp.Role.RoleLastUsed.Region != nil { + f8.Region = resp.Role.RoleLastUsed.Region + } + cr.Status.AtProvider.RoleLastUsed = f8 + } else { + cr.Status.AtProvider.RoleLastUsed = nil + } + if resp.Role.RoleName != nil { + cr.Status.AtProvider.RoleName = resp.Role.RoleName + } else { + cr.Status.AtProvider.RoleName = nil + } + if resp.Role.Tags != nil { + f10 := []*svcapitypes.Tag{} + for _, f10iter := range resp.Role.Tags { + f10elem := &svcapitypes.Tag{} + if f10iter.Key != nil { + f10elem.Key = f10iter.Key + } + if f10iter.Value != nil { + f10elem.Value = f10iter.Value + } + f10 = append(f10, f10elem) + } + cr.Status.AtProvider.Tags = f10 + } else { + cr.Status.AtProvider.Tags = nil + } + + return e.postCreate(ctx, cr, resp, managed.ExternalCreation{}, err) +} + +func (e *external) Update(ctx context.Context, mg cpresource.Managed) (managed.ExternalUpdate, error) { + return e.update(ctx, mg) + +} + +func (e *external) Delete(ctx context.Context, mg cpresource.Managed) error { + cr, ok := mg.(*svcapitypes.ServiceLinkedRole) + if !ok { + return errors.New(errUnexpectedObject) + } + cr.Status.SetConditions(xpv1.Deleting()) + input := GenerateDeleteServiceLinkedRoleInput(cr) + ignore, err := e.preDelete(ctx, cr, input) + if err != nil { + return errors.Wrap(err, "pre-delete failed") + } + if ignore { + return nil + } + resp, err := e.client.DeleteServiceLinkedRoleWithContext(ctx, input) + return e.postDelete(ctx, cr, resp, awsclient.Wrap(cpresource.Ignore(IsNotFound, err), errDelete)) +} + +type option func(*external) + +func newExternal(kube client.Client, client svcsdkapi.IAMAPI, opts []option) *external { + e := &external{ + kube: kube, + client: client, + observe: nopObserve, + preCreate: nopPreCreate, + postCreate: nopPostCreate, + preDelete: nopPreDelete, + postDelete: nopPostDelete, + update: nopUpdate, + } + for _, f := range opts { + f(e) + } + return e +} + +type external struct { + kube client.Client + client svcsdkapi.IAMAPI + observe func(context.Context, cpresource.Managed) (managed.ExternalObservation, error) + preCreate func(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.CreateServiceLinkedRoleInput) error + postCreate func(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.CreateServiceLinkedRoleOutput, managed.ExternalCreation, error) (managed.ExternalCreation, error) + preDelete func(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.DeleteServiceLinkedRoleInput) (bool, error) + postDelete func(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.DeleteServiceLinkedRoleOutput, error) error + update func(context.Context, cpresource.Managed) (managed.ExternalUpdate, error) +} + +func nopObserve(context.Context, cpresource.Managed) (managed.ExternalObservation, error) { + return managed.ExternalObservation{}, nil +} + +func nopPreCreate(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.CreateServiceLinkedRoleInput) error { + return nil +} +func nopPostCreate(_ context.Context, _ *svcapitypes.ServiceLinkedRole, _ *svcsdk.CreateServiceLinkedRoleOutput, cre managed.ExternalCreation, err error) (managed.ExternalCreation, error) { + return cre, err +} +func nopPreDelete(context.Context, *svcapitypes.ServiceLinkedRole, *svcsdk.DeleteServiceLinkedRoleInput) (bool, error) { + return false, nil +} +func nopPostDelete(_ context.Context, _ *svcapitypes.ServiceLinkedRole, _ *svcsdk.DeleteServiceLinkedRoleOutput, err error) error { + return err +} +func nopUpdate(context.Context, cpresource.Managed) (managed.ExternalUpdate, error) { + return managed.ExternalUpdate{}, nil +} diff --git a/pkg/controller/iam/servicelinkedrole/zz_conversions.go b/pkg/controller/iam/servicelinkedrole/zz_conversions.go new file mode 100644 index 0000000000..3f5a31f82e --- /dev/null +++ b/pkg/controller/iam/servicelinkedrole/zz_conversions.go @@ -0,0 +1,63 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by ack-generate. DO NOT EDIT. + +package servicelinkedrole + +import ( + "github.com/aws/aws-sdk-go/aws/awserr" + svcsdk "github.com/aws/aws-sdk-go/service/iam" + + svcapitypes "github.com/crossplane-contrib/provider-aws/apis/iam/v1alpha1" +) + +// NOTE(muvaf): We return pointers in case the function needs to start with an +// empty object, hence need to return a new pointer. + +// GenerateCreateServiceLinkedRoleInput returns a create input. +func GenerateCreateServiceLinkedRoleInput(cr *svcapitypes.ServiceLinkedRole) *svcsdk.CreateServiceLinkedRoleInput { + res := &svcsdk.CreateServiceLinkedRoleInput{} + + if cr.Spec.ForProvider.AWSServiceName != nil { + res.SetAWSServiceName(*cr.Spec.ForProvider.AWSServiceName) + } + if cr.Spec.ForProvider.CustomSuffix != nil { + res.SetCustomSuffix(*cr.Spec.ForProvider.CustomSuffix) + } + if cr.Spec.ForProvider.Description != nil { + res.SetDescription(*cr.Spec.ForProvider.Description) + } + + return res +} + +// GenerateDeleteServiceLinkedRoleInput returns a deletion input. +func GenerateDeleteServiceLinkedRoleInput(cr *svcapitypes.ServiceLinkedRole) *svcsdk.DeleteServiceLinkedRoleInput { + res := &svcsdk.DeleteServiceLinkedRoleInput{} + + if cr.Status.AtProvider.RoleName != nil { + res.SetRoleName(*cr.Status.AtProvider.RoleName) + } + + return res +} + +// IsNotFound returns whether the given error is of type NotFound or not. +func IsNotFound(err error) bool { + awsErr, ok := err.(awserr.Error) + return ok && awsErr.Code() == "NoSuchEntity" +}